runtime: mark go-context.S as no-executable-stack and split-stack supported
[gcc.git] / gcc / tree-ssa-sccvn.c
index 4a2f70293b885bf6ba419bca62b760bdf9baadb6..44b8c67b198c300c7449bac87827db25032c3829 100644 (file)
@@ -1,5 +1,5 @@
 /* SCC value numbering for trees
-   Copyright (C) 2006-2018 Free Software Foundation, Inc.
+   Copyright (C) 2006-2019 Free Software Foundation, Inc.
    Contributed by Daniel Berlin <dan@dberlin.org>
 
 This file is part of GCC.
@@ -21,11 +21,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "splay-tree.h"
 #include "backend.h"
 #include "rtl.h"
 #include "tree.h"
 #include "gimple.h"
-#include "alloc-pool.h"
 #include "ssa.h"
 #include "expmed.h"
 #include "insn-config.h"
@@ -69,6 +69,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfgcleanup.h"
 #include "tree-ssa-loop.h"
 #include "tree-scalar-evolution.h"
+#include "tree-ssa-loop-niter.h"
+#include "builtins.h"
 #include "tree-ssa-sccvn.h"
 
 /* This algorithm is based on the SCC algorithm presented by Keith
@@ -128,11 +130,10 @@ along with GCC; see the file COPYING3.  If not see
    structure copies.
 */
 
+/* There's no BB_EXECUTABLE but we can use BB_VISITED.  */
+#define BB_EXECUTABLE BB_VISITED
 
-static tree *last_vuse_ptr;
-static vn_lookup_kind vn_walk_kind;
 static vn_lookup_kind default_vn_walk_kind;
-bitmap const_parms;
 
 /* vn_nary_op hashtable helpers.  */
 
@@ -157,7 +158,7 @@ vn_nary_op_hasher::hash (const vn_nary_op_s *vno1)
 inline bool
 vn_nary_op_hasher::equal (const vn_nary_op_s *vno1, const vn_nary_op_s *vno2)
 {
-  return vn_nary_op_eq (vno1, vno2);
+  return vno1 == vno2 || vn_nary_op_eq (vno1, vno2);
 }
 
 typedef hash_table<vn_nary_op_hasher> vn_nary_op_table_type;
@@ -169,11 +170,10 @@ typedef vn_nary_op_table_type::iterator vn_nary_op_iterator_type;
 static int
 vn_phi_eq (const_vn_phi_t const vp1, const_vn_phi_t const vp2);
 
-struct vn_phi_hasher : pointer_hash <vn_phi_s>
+struct vn_phi_hasher : nofree_ptr_hash <vn_phi_s>
 { 
   static inline hashval_t hash (const vn_phi_s *);
   static inline bool equal (const vn_phi_s *, const vn_phi_s *);
-  static inline void remove (vn_phi_s *);
 };
 
 /* Return the computed hashcode for phi operation P1.  */
@@ -189,15 +189,7 @@ vn_phi_hasher::hash (const vn_phi_s *vp1)
 inline bool
 vn_phi_hasher::equal (const vn_phi_s *vp1, const vn_phi_s *vp2)
 {
-  return vn_phi_eq (vp1, vp2);
-}
-
-/* Free a phi operation structure VP.  */
-
-inline void
-vn_phi_hasher::remove (vn_phi_s *phi)
-{
-  phi->phiargs.release ();
+  return vp1 == vp2 || vn_phi_eq (vp1, vp2);
 }
 
 typedef hash_table<vn_phi_hasher> vn_phi_table_type;
@@ -235,11 +227,10 @@ free_reference (vn_reference_s *vr)
 
 /* vn_reference hashtable helpers.  */
 
-struct vn_reference_hasher : pointer_hash <vn_reference_s>
+struct vn_reference_hasher : nofree_ptr_hash <vn_reference_s>
 {
   static inline hashval_t hash (const vn_reference_s *);
   static inline bool equal (const vn_reference_s *, const vn_reference_s *);
-  static inline void remove (vn_reference_s *);
 };
 
 /* Return the hashcode for a given reference operation P1.  */
@@ -253,29 +244,20 @@ vn_reference_hasher::hash (const vn_reference_s *vr1)
 inline bool
 vn_reference_hasher::equal (const vn_reference_s *v, const vn_reference_s *c)
 {
-  return vn_reference_eq (v, c);
-}
-
-inline void
-vn_reference_hasher::remove (vn_reference_s *v)
-{
-  free_reference (v);
+  return v == c || vn_reference_eq (v, c);
 }
 
 typedef hash_table<vn_reference_hasher> vn_reference_table_type;
 typedef vn_reference_table_type::iterator vn_reference_iterator_type;
 
 
-/* The set of hashtables and alloc_pool's for their items.  */
+/* The set of VN hashtables.  */
 
 typedef struct vn_tables_s
 {
   vn_nary_op_table_type *nary;
   vn_phi_table_type *phis;
   vn_reference_table_type *references;
-  struct obstack nary_obstack;
-  object_allocator<vn_phi_s> *phis_pool;
-  object_allocator<vn_reference_s> *references_pool;
 } *vn_tables_t;
 
 
@@ -310,52 +292,24 @@ static hash_table<vn_constant_hasher> *constant_to_value_id;
 static bitmap constant_value_ids;
 
 
+/* Obstack we allocate the vn-tables elements from.  */
+static obstack vn_tables_obstack;
+/* Special obstack we never unwind.  */
+static obstack vn_tables_insert_obstack;
+
+static vn_reference_t last_inserted_ref;
+static vn_phi_t last_inserted_phi;
+static vn_nary_op_t last_inserted_nary;
+
 /* Valid hashtables storing information we have proven to be
    correct.  */
-
 static vn_tables_t valid_info;
 
-/* Optimistic hashtables storing information we are making assumptions about
-   during iterations.  */
-
-static vn_tables_t optimistic_info;
-
-/* Pointer to the set of hashtables that is currently being used.
-   Should always point to either the optimistic_info, or the
-   valid_info.  */
-
-static vn_tables_t current_info;
-
-
-/* Reverse post order index for each basic block.  */
-
-static int *rpo_numbers;
 
-#define SSA_VAL(x) (VN_INFO ((x))->valnum)
+/* Valueization hook.  Valueize NAME if it is an SSA name, otherwise
+   just return it.  */
+tree (*vn_valueize) (tree);
 
-/* Return the SSA value of the VUSE x, supporting released VDEFs
-   during elimination which will value-number the VDEF to the
-   associated VUSE (but not substitute in the whole lattice).  */
-
-static inline tree
-vuse_ssa_val (tree x)
-{
-  if (!x)
-    return NULL_TREE;
-
-  do
-    {
-      tree tem = SSA_VAL (x);
-      /* stmt walking can walk over a backedge and reach code we didn't
-        value-number yet.  */
-      if (tem == VN_TOP)
-       return x;
-      x = tem;
-    }
-  while (SSA_NAME_IN_FREE_LIST (x));
-
-  return x;
-}
 
 /* This represents the top of the VN lattice, which is the universal
    value.  */
@@ -366,66 +320,190 @@ tree VN_TOP;
 
 static unsigned int next_value_id;
 
-/* Next DFS number and the stack for strongly connected component
-   detection. */
-
-static unsigned int next_dfs_num;
-static vec<tree> sccstack;
-
-
 
 /* Table of vn_ssa_aux_t's, one per ssa_name.  The vn_ssa_aux_t objects
    are allocated on an obstack for locality reasons, and to free them
    without looping over the vec.  */
 
-static vec<vn_ssa_aux_t> vn_ssa_aux_table;
+struct vn_ssa_aux_hasher : typed_noop_remove <vn_ssa_aux_t>
+{
+  typedef vn_ssa_aux_t value_type;
+  typedef tree compare_type;
+  static inline hashval_t hash (const value_type &);
+  static inline bool equal (const value_type &, const compare_type &);
+  static inline void mark_deleted (value_type &) {}
+  static inline void mark_empty (value_type &e) { e = NULL; }
+  static inline bool is_deleted (value_type &) { return false; }
+  static inline bool is_empty (value_type &e) { return e == NULL; }
+};
+
+hashval_t
+vn_ssa_aux_hasher::hash (const value_type &entry)
+{
+  return SSA_NAME_VERSION (entry->name);
+}
+
+bool
+vn_ssa_aux_hasher::equal (const value_type &entry, const compare_type &name)
+{
+  return name == entry->name;
+}
+
+static hash_table<vn_ssa_aux_hasher> *vn_ssa_aux_hash;
+typedef hash_table<vn_ssa_aux_hasher>::iterator vn_ssa_aux_iterator_type;
 static struct obstack vn_ssa_aux_obstack;
 
+static vn_nary_op_t vn_nary_op_insert_stmt (gimple *, tree);
+static unsigned int vn_nary_length_from_stmt (gimple *);
+static vn_nary_op_t alloc_vn_nary_op_noinit (unsigned int, obstack *);
+static vn_nary_op_t vn_nary_op_insert_into (vn_nary_op_t,
+                                           vn_nary_op_table_type *, bool);
+static void init_vn_nary_op_from_stmt (vn_nary_op_t, gimple *);
+static void init_vn_nary_op_from_pieces (vn_nary_op_t, unsigned int,
+                                        enum tree_code, tree, tree *);
+static tree vn_lookup_simplify_result (gimple_match_op *);
+static vn_reference_t vn_reference_lookup_or_insert_for_pieces
+         (tree, alias_set_type, tree, vec<vn_reference_op_s, va_heap>, tree);
+
 /* Return whether there is value numbering information for a given SSA name.  */
 
 bool
 has_VN_INFO (tree name)
 {
-  if (SSA_NAME_VERSION (name) < vn_ssa_aux_table.length ())
-    return vn_ssa_aux_table[SSA_NAME_VERSION (name)] != NULL;
-  return false;
+  return vn_ssa_aux_hash->find_with_hash (name, SSA_NAME_VERSION (name));
 }
 
-/* Return the value numbering information for a given SSA name.  */
-
 vn_ssa_aux_t
 VN_INFO (tree name)
 {
-  vn_ssa_aux_t res = vn_ssa_aux_table[SSA_NAME_VERSION (name)];
-  gcc_checking_assert (res);
-  return res;
+  vn_ssa_aux_t *res
+    = vn_ssa_aux_hash->find_slot_with_hash (name, SSA_NAME_VERSION (name),
+                                           INSERT);
+  if (*res != NULL)
+    return *res;
+
+  vn_ssa_aux_t newinfo = *res = XOBNEW (&vn_ssa_aux_obstack, struct vn_ssa_aux);
+  memset (newinfo, 0, sizeof (struct vn_ssa_aux));
+  newinfo->name = name;
+  newinfo->valnum = VN_TOP;
+  /* We are using the visited flag to handle uses with defs not within the
+     region being value-numbered.  */
+  newinfo->visited = false;
+
+  /* Given we create the VN_INFOs on-demand now we have to do initialization
+     different than VN_TOP here.  */
+  if (SSA_NAME_IS_DEFAULT_DEF (name))
+    switch (TREE_CODE (SSA_NAME_VAR (name)))
+      {
+      case VAR_DECL:
+        /* All undefined vars are VARYING.  */
+        newinfo->valnum = name;
+       newinfo->visited = true;
+       break;
+
+      case PARM_DECL:
+       /* Parameters are VARYING but we can record a condition
+          if we know it is a non-NULL pointer.  */
+       newinfo->visited = true;
+       newinfo->valnum = name;
+       if (POINTER_TYPE_P (TREE_TYPE (name))
+           && nonnull_arg_p (SSA_NAME_VAR (name)))
+         {
+           tree ops[2];
+           ops[0] = name;
+           ops[1] = build_int_cst (TREE_TYPE (name), 0);
+           vn_nary_op_t nary;
+           /* Allocate from non-unwinding stack.  */
+           nary = alloc_vn_nary_op_noinit (2, &vn_tables_insert_obstack);
+           init_vn_nary_op_from_pieces (nary, 2, NE_EXPR,
+                                        boolean_type_node, ops);
+           nary->predicated_values = 0;
+           nary->u.result = boolean_true_node;
+           vn_nary_op_insert_into (nary, valid_info->nary, true);
+           gcc_assert (nary->unwind_to == NULL);
+           /* Also do not link it into the undo chain.  */
+           last_inserted_nary = nary->next;
+           nary->next = (vn_nary_op_t)(void *)-1;
+           nary = alloc_vn_nary_op_noinit (2, &vn_tables_insert_obstack);
+           init_vn_nary_op_from_pieces (nary, 2, EQ_EXPR,
+                                        boolean_type_node, ops);
+           nary->predicated_values = 0;
+           nary->u.result = boolean_false_node;
+           vn_nary_op_insert_into (nary, valid_info->nary, true);
+           gcc_assert (nary->unwind_to == NULL);
+           last_inserted_nary = nary->next;
+           nary->next = (vn_nary_op_t)(void *)-1;
+           if (dump_file && (dump_flags & TDF_DETAILS))
+             {
+               fprintf (dump_file, "Recording ");
+               print_generic_expr (dump_file, name, TDF_SLIM);
+               fprintf (dump_file, " != 0\n");
+             }
+         }
+       break;
+
+      case RESULT_DECL:
+       /* If the result is passed by invisible reference the default
+          def is initialized, otherwise it's uninitialized.  Still
+          undefined is varying.  */
+       newinfo->visited = true;
+       newinfo->valnum = name;
+       break;
+
+      default:
+       gcc_unreachable ();
+      }
+  return newinfo;
 }
 
-/* Set the value numbering info for a given SSA name to a given
-   value.  */
+/* Return the SSA value of X.  */
 
-static inline void
-VN_INFO_SET (tree name, vn_ssa_aux_t value)
+inline tree
+SSA_VAL (tree x, bool *visited = NULL)
 {
-  vn_ssa_aux_table[SSA_NAME_VERSION (name)] = value;
+  vn_ssa_aux_t tem = vn_ssa_aux_hash->find_with_hash (x, SSA_NAME_VERSION (x));
+  if (visited)
+    *visited = tem && tem->visited;
+  return tem && tem->visited ? tem->valnum : x;
 }
 
-/* Initialize the value numbering info for a given SSA name.
-   This should be called just once for every SSA name.  */
+/* Return the SSA value of the VUSE x, supporting released VDEFs
+   during elimination which will value-number the VDEF to the
+   associated VUSE (but not substitute in the whole lattice).  */
 
-vn_ssa_aux_t
-VN_INFO_GET (tree name)
+static inline tree
+vuse_ssa_val (tree x)
 {
-  vn_ssa_aux_t newinfo;
+  if (!x)
+    return NULL_TREE;
 
-  gcc_assert (SSA_NAME_VERSION (name) >= vn_ssa_aux_table.length ()
-             || vn_ssa_aux_table[SSA_NAME_VERSION (name)] == NULL);
-  newinfo = XOBNEW (&vn_ssa_aux_obstack, struct vn_ssa_aux);
-  memset (newinfo, 0, sizeof (struct vn_ssa_aux));
-  if (SSA_NAME_VERSION (name) >= vn_ssa_aux_table.length ())
-    vn_ssa_aux_table.safe_grow_cleared (SSA_NAME_VERSION (name) + 1);
-  vn_ssa_aux_table[SSA_NAME_VERSION (name)] = newinfo;
-  return newinfo;
+  do
+    {
+      x = SSA_VAL (x);
+      gcc_assert (x != VN_TOP);
+    }
+  while (SSA_NAME_IN_FREE_LIST (x));
+
+  return x;
+}
+
+/* Similar to the above but used as callback for walk_non_aliases_vuses
+   and thus should stop at unvisited VUSE to not walk across region
+   boundaries.  */
+
+static tree
+vuse_valueize (tree vuse)
+{
+  do
+    {
+      bool visited;
+      vuse = SSA_VAL (vuse, &visited);
+      if (!visited)
+       return NULL_TREE;
+      gcc_assert (vuse != VN_TOP);
+    }
+  while (SSA_NAME_IN_FREE_LIST (vuse));
+  return vuse;
 }
 
 
@@ -514,6 +592,11 @@ get_or_alloc_constant_value_id (tree constant)
   struct vn_constant_s vc;
   vn_constant_t vcp;
 
+  /* If the hashtable isn't initialized we're not running from PRE and thus
+     do not need value-ids.  */
+  if (!constant_to_value_id)
+    return 0;
+
   vc.hashcode = vn_hash_constant_with_type (constant);
   vc.constant = constant;
   slot = constant_to_value_id->find_slot (&vc, INSERT);
@@ -710,39 +793,6 @@ vn_reference_eq (const_vn_reference_t const vr1, const_vn_reference_t const vr2)
 static void
 copy_reference_ops_from_ref (tree ref, vec<vn_reference_op_s> *result)
 {
-  if (TREE_CODE (ref) == TARGET_MEM_REF)
-    {
-      vn_reference_op_s temp;
-
-      result->reserve (3);
-
-      memset (&temp, 0, sizeof (temp));
-      temp.type = TREE_TYPE (ref);
-      temp.opcode = TREE_CODE (ref);
-      temp.op0 = TMR_INDEX (ref);
-      temp.op1 = TMR_STEP (ref);
-      temp.op2 = TMR_OFFSET (ref);
-      temp.off = -1;
-      temp.clique = MR_DEPENDENCE_CLIQUE (ref);
-      temp.base = MR_DEPENDENCE_BASE (ref);
-      result->quick_push (temp);
-
-      memset (&temp, 0, sizeof (temp));
-      temp.type = NULL_TREE;
-      temp.opcode = ERROR_MARK;
-      temp.op0 = TMR_INDEX2 (ref);
-      temp.off = -1;
-      result->quick_push (temp);
-
-      memset (&temp, 0, sizeof (temp));
-      temp.type = NULL_TREE;
-      temp.opcode = TREE_CODE (TMR_BASE (ref));
-      temp.op0 = TMR_BASE (ref);
-      temp.off = -1;
-      result->quick_push (temp);
-      return;
-    }
-
   /* For non-calls, store the information that makes up the address.  */
   tree orig = ref;
   while (ref)
@@ -772,6 +822,20 @@ copy_reference_ops_from_ref (tree ref, vec<vn_reference_op_s> *result)
          temp.base = MR_DEPENDENCE_BASE (ref);
          temp.reverse = REF_REVERSE_STORAGE_ORDER (ref);
          break;
+       case TARGET_MEM_REF:
+         /* The base address gets its own vn_reference_op_s structure.  */
+         temp.op0 = TMR_INDEX (ref);
+         temp.op1 = TMR_STEP (ref);
+         temp.op2 = TMR_OFFSET (ref);
+         temp.clique = MR_DEPENDENCE_CLIQUE (ref);
+         temp.base = MR_DEPENDENCE_BASE (ref);
+         result->safe_push (temp);
+         memset (&temp, 0, sizeof (temp));
+         temp.type = NULL_TREE;
+         temp.opcode = ERROR_MARK;
+         temp.op0 = TMR_INDEX2 (ref);
+         temp.off = -1;
+         break;
        case BIT_FIELD_REF:
          /* Record bits, position and storage order.  */
          temp.op0 = TREE_OPERAND (ref, 1);
@@ -1126,11 +1190,11 @@ copy_reference_ops_from_call (gcall *call,
 
   /* Copy the type, opcode, function, static chain and EH region, if any.  */
   memset (&temp, 0, sizeof (temp));
-  temp.type = gimple_call_return_type (call);
+  temp.type = gimple_call_fntype (call);
   temp.opcode = CALL_EXPR;
   temp.op0 = gimple_call_fn (call);
   temp.op1 = gimple_call_chain (call);
-  if (stmt_could_throw_p (call) && (lr = lookup_stmt_eh_lp (call)) > 0)
+  if (stmt_could_throw_p (cfun, call) && (lr = lookup_stmt_eh_lp (call)) > 0)
     temp.op2 = size_int (lr);
   temp.off = -1;
   result->safe_push (temp);
@@ -1185,107 +1249,123 @@ static bool
 vn_reference_maybe_forwprop_address (vec<vn_reference_op_s> *ops,
                                     unsigned int *i_p)
 {
-  unsigned int i = *i_p;
-  vn_reference_op_t op = &(*ops)[i];
-  vn_reference_op_t mem_op = &(*ops)[i - 1];
-  gimple *def_stmt;
-  enum tree_code code;
-  poly_offset_int off;
-
-  def_stmt = SSA_NAME_DEF_STMT (op->op0);
-  if (!is_gimple_assign (def_stmt))
-    return false;
-
-  code = gimple_assign_rhs_code (def_stmt);
-  if (code != ADDR_EXPR
-      && code != POINTER_PLUS_EXPR)
-    return false;
-
-  off = poly_offset_int::from (wi::to_poly_wide (mem_op->op0), SIGNED);
+  bool changed = false;
+  vn_reference_op_t op;
 
-  /* The only thing we have to do is from &OBJ.foo.bar add the offset
-     from .foo.bar to the preceding MEM_REF offset and replace the
-     address with &OBJ.  */
-  if (code == ADDR_EXPR)
+  do
     {
-      tree addr, addr_base;
-      poly_int64 addr_offset;
-
-      addr = gimple_assign_rhs1 (def_stmt);
-      addr_base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0),
-                                                &addr_offset);
-      /* If that didn't work because the address isn't invariant propagate
-         the reference tree from the address operation in case the current
-        dereference isn't offsetted.  */
-      if (!addr_base
-         && *i_p == ops->length () - 1
-         && known_eq (off, 0)
-         /* This makes us disable this transform for PRE where the
-            reference ops might be also used for code insertion which
-            is invalid.  */
-         && default_vn_walk_kind == VN_WALKREWRITE)
+      unsigned int i = *i_p;
+      op = &(*ops)[i];
+      vn_reference_op_t mem_op = &(*ops)[i - 1];
+      gimple *def_stmt;
+      enum tree_code code;
+      poly_offset_int off;
+
+      def_stmt = SSA_NAME_DEF_STMT (op->op0);
+      if (!is_gimple_assign (def_stmt))
+       return changed;
+
+      code = gimple_assign_rhs_code (def_stmt);
+      if (code != ADDR_EXPR
+         && code != POINTER_PLUS_EXPR)
+       return changed;
+
+      off = poly_offset_int::from (wi::to_poly_wide (mem_op->op0), SIGNED);
+
+      /* The only thing we have to do is from &OBJ.foo.bar add the offset
+        from .foo.bar to the preceding MEM_REF offset and replace the
+        address with &OBJ.  */
+      if (code == ADDR_EXPR)
        {
-         auto_vec<vn_reference_op_s, 32> tem;
-         copy_reference_ops_from_ref (TREE_OPERAND (addr, 0), &tem);
-         /* Make sure to preserve TBAA info.  The only objects not
-            wrapped in MEM_REFs that can have their address taken are
-            STRING_CSTs.  */
-         if (tem.length () >= 2
-             && tem[tem.length () - 2].opcode == MEM_REF)
+         tree addr, addr_base;
+         poly_int64 addr_offset;
+
+         addr = gimple_assign_rhs1 (def_stmt);
+         addr_base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0),
+                                                    &addr_offset);
+         /* If that didn't work because the address isn't invariant propagate
+            the reference tree from the address operation in case the current
+            dereference isn't offsetted.  */
+         if (!addr_base
+             && *i_p == ops->length () - 1
+             && known_eq (off, 0)
+             /* This makes us disable this transform for PRE where the
+                reference ops might be also used for code insertion which
+                is invalid.  */
+             && default_vn_walk_kind == VN_WALKREWRITE)
            {
-             vn_reference_op_t new_mem_op = &tem[tem.length () - 2];
-             new_mem_op->op0
-               = wide_int_to_tree (TREE_TYPE (mem_op->op0),
-                                   wi::to_poly_wide (new_mem_op->op0));
+             auto_vec<vn_reference_op_s, 32> tem;
+             copy_reference_ops_from_ref (TREE_OPERAND (addr, 0), &tem);
+             /* Make sure to preserve TBAA info.  The only objects not
+                wrapped in MEM_REFs that can have their address taken are
+                STRING_CSTs.  */
+             if (tem.length () >= 2
+                 && tem[tem.length () - 2].opcode == MEM_REF)
+               {
+                 vn_reference_op_t new_mem_op = &tem[tem.length () - 2];
+                 new_mem_op->op0
+                     = wide_int_to_tree (TREE_TYPE (mem_op->op0),
+                                         wi::to_poly_wide (new_mem_op->op0));
+               }
+             else
+               gcc_assert (tem.last ().opcode == STRING_CST);
+             ops->pop ();
+             ops->pop ();
+             ops->safe_splice (tem);
+             --*i_p;
+             return true;
            }
-         else
-           gcc_assert (tem.last ().opcode == STRING_CST);
-         ops->pop ();
-         ops->pop ();
-         ops->safe_splice (tem);
-         --*i_p;
-         return true;
+         if (!addr_base
+             || TREE_CODE (addr_base) != MEM_REF
+             || (TREE_CODE (TREE_OPERAND (addr_base, 0)) == SSA_NAME
+                 && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (addr_base,
+                                                                   0))))
+           return changed;
+
+         off += addr_offset;
+         off += mem_ref_offset (addr_base);
+         op->op0 = TREE_OPERAND (addr_base, 0);
+       }
+      else
+       {
+         tree ptr, ptroff;
+         ptr = gimple_assign_rhs1 (def_stmt);
+         ptroff = gimple_assign_rhs2 (def_stmt);
+         if (TREE_CODE (ptr) != SSA_NAME
+             || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)
+             /* Make sure to not endlessly recurse.
+                See gcc.dg/tree-ssa/20040408-1.c for an example.  Can easily
+                happen when we value-number a PHI to its backedge value.  */
+             || SSA_VAL (ptr) == op->op0
+             || !poly_int_tree_p (ptroff))
+           return changed;
+
+         off += wi::to_poly_offset (ptroff);
+         op->op0 = ptr;
        }
-      if (!addr_base
-         || TREE_CODE (addr_base) != MEM_REF
-         || (TREE_CODE (TREE_OPERAND (addr_base, 0)) == SSA_NAME
-             && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (addr_base, 0))))
-       return false;
-
-      off += addr_offset;
-      off += mem_ref_offset (addr_base);
-      op->op0 = TREE_OPERAND (addr_base, 0);
-    }
-  else
-    {
-      tree ptr, ptroff;
-      ptr = gimple_assign_rhs1 (def_stmt);
-      ptroff = gimple_assign_rhs2 (def_stmt);
-      if (TREE_CODE (ptr) != SSA_NAME
-         || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)
-         || !poly_int_tree_p (ptroff))
-       return false;
 
-      off += wi::to_poly_offset (ptroff);
-      op->op0 = ptr;
+      mem_op->op0 = wide_int_to_tree (TREE_TYPE (mem_op->op0), off);
+      if (tree_fits_shwi_p (mem_op->op0))
+       mem_op->off = tree_to_shwi (mem_op->op0);
+      else
+       mem_op->off = -1;
+      /* ???  Can end up with endless recursion here!?
+        gcc.c-torture/execute/strcmp-1.c  */
+      if (TREE_CODE (op->op0) == SSA_NAME)
+       op->op0 = SSA_VAL (op->op0);
+      if (TREE_CODE (op->op0) != SSA_NAME)
+       op->opcode = TREE_CODE (op->op0);
+
+      changed = true;
     }
+  /* Tail-recurse.  */
+  while (TREE_CODE (op->op0) == SSA_NAME);
 
-  mem_op->op0 = wide_int_to_tree (TREE_TYPE (mem_op->op0), off);
-  if (tree_fits_shwi_p (mem_op->op0))
-    mem_op->off = tree_to_shwi (mem_op->op0);
-  else
-    mem_op->off = -1;
-  if (TREE_CODE (op->op0) == SSA_NAME)
-    op->op0 = SSA_VAL (op->op0);
-  if (TREE_CODE (op->op0) != SSA_NAME)
-    op->opcode = TREE_CODE (op->op0);
-
-  /* And recurse.  */
-  if (TREE_CODE (op->op0) == SSA_NAME)
-    vn_reference_maybe_forwprop_address (ops, i_p);
-  else if (TREE_CODE (op->op0) == ADDR_EXPR)
+  /* Fold a remaining *&.  */
+  if (TREE_CODE (op->op0) == ADDR_EXPR)
     vn_reference_fold_indirect (ops, i_p);
-  return true;
+
+  return changed;
 }
 
 /* Optimize the reference REF to a constant if possible or return
@@ -1303,7 +1383,7 @@ fully_constant_vn_reference_p (vn_reference_t ref)
   if (op->opcode == CALL_EXPR
       && TREE_CODE (op->op0) == ADDR_EXPR
       && TREE_CODE (TREE_OPERAND (op->op0, 0)) == FUNCTION_DECL
-      && DECL_BUILT_IN (TREE_OPERAND (op->op0, 0))
+      && fndecl_built_in_p (TREE_OPERAND (op->op0, 0))
       && operands.length () >= 2
       && operands.length () <= 3)
     {
@@ -1338,16 +1418,17 @@ fully_constant_vn_reference_p (vn_reference_t ref)
 
   /* Simplify reads from constants or constant initializers.  */
   else if (BITS_PER_UNIT == 8
-          && is_gimple_reg_type (ref->type)
-          && (!INTEGRAL_TYPE_P (ref->type)
-              || TYPE_PRECISION (ref->type) % BITS_PER_UNIT == 0))
+          && COMPLETE_TYPE_P (ref->type)
+          && is_gimple_reg_type (ref->type))
     {
       poly_int64 off = 0;
       HOST_WIDE_INT size;
       if (INTEGRAL_TYPE_P (ref->type))
        size = TYPE_PRECISION (ref->type);
-      else
+      else if (tree_fits_shwi_p (TYPE_SIZE (ref->type)))
        size = tree_to_shwi (TYPE_SIZE (ref->type));
+      else
+       return NULL_TREE;
       if (size % BITS_PER_UNIT != 0
          || size > MAX_BITSIZE_MODE_ANY_MODE)
        return NULL_TREE;
@@ -1437,7 +1518,8 @@ contains_storage_order_barrier_p (vec<vn_reference_op_s> ops)
    whether any operands were valueized.  */
 
 static vec<vn_reference_op_s> 
-valueize_refs_1 (vec<vn_reference_op_s> orig, bool *valueized_anything)
+valueize_refs_1 (vec<vn_reference_op_s> orig, bool *valueized_anything,
+                bool with_avail = false)
 {
   vn_reference_op_t vro;
   unsigned int i;
@@ -1449,7 +1531,7 @@ valueize_refs_1 (vec<vn_reference_op_s> orig, bool *valueized_anything)
       if (vro->opcode == SSA_NAME
          || (vro->op0 && TREE_CODE (vro->op0) == SSA_NAME))
        {
-         tree tem = SSA_VAL (vro->op0);
+         tree tem = with_avail ? vn_valueize (vro->op0) : SSA_VAL (vro->op0);
          if (tem != vro->op0)
            {
              *valueized_anything = true;
@@ -1462,7 +1544,7 @@ valueize_refs_1 (vec<vn_reference_op_s> orig, bool *valueized_anything)
        }
       if (vro->op1 && TREE_CODE (vro->op1) == SSA_NAME)
        {
-         tree tem = SSA_VAL (vro->op1);
+         tree tem = with_avail ? vn_valueize (vro->op1) : SSA_VAL (vro->op1);
          if (tem != vro->op1)
            {
              *valueized_anything = true;
@@ -1471,7 +1553,7 @@ valueize_refs_1 (vec<vn_reference_op_s> orig, bool *valueized_anything)
        }
       if (vro->op2 && TREE_CODE (vro->op2) == SSA_NAME)
        {
-         tree tem = SSA_VAL (vro->op2);
+         tree tem = with_avail ? vn_valueize (vro->op2) : SSA_VAL (vro->op2);
          if (tem != vro->op2)
            {
              *valueized_anything = true;
@@ -1567,9 +1649,7 @@ vn_reference_lookup_1 (vn_reference_t vr, vn_reference_t *vnresult)
   hashval_t hash;
 
   hash = vr->hashcode;
-  slot = current_info->references->find_slot_with_hash (vr, hash, NO_INSERT);
-  if (!slot && current_info == optimistic_info)
-    slot = valid_info->references->find_slot_with_hash (vr, hash, NO_INSERT);
+  slot = valid_info->references->find_slot_with_hash (vr, hash, NO_INSERT);
   if (slot)
     {
       if (vnresult)
@@ -1580,25 +1660,253 @@ vn_reference_lookup_1 (vn_reference_t vr, vn_reference_t *vnresult)
   return NULL_TREE;
 }
 
+
+/* Partial definition tracking support.  */
+
+struct pd_range
+{
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT size;
+};
+
+struct pd_data
+{
+  tree rhs;
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT size;
+};
+
+/* Context for alias walking.  */
+
+struct vn_walk_cb_data
+{
+  vn_walk_cb_data (vn_reference_t vr_, tree orig_ref_, tree *last_vuse_ptr_,
+                  vn_lookup_kind vn_walk_kind_, bool tbaa_p_)
+    : vr (vr_), last_vuse_ptr (last_vuse_ptr_),
+      vn_walk_kind (vn_walk_kind_), tbaa_p (tbaa_p_), known_ranges (NULL)
+   {
+     ao_ref_init (&orig_ref, orig_ref_);
+   }
+  ~vn_walk_cb_data ();
+  void *push_partial_def (const pd_data& pd, tree, HOST_WIDE_INT);
+
+  vn_reference_t vr;
+  ao_ref orig_ref;
+  tree *last_vuse_ptr;
+  vn_lookup_kind vn_walk_kind;
+  bool tbaa_p;
+
+  /* The VDEFs of partial defs we come along.  */
+  auto_vec<pd_data, 2> partial_defs;
+  /* The first defs range to avoid splay tree setup in most cases.  */
+  pd_range first_range;
+  tree first_vuse;
+  splay_tree known_ranges;
+  obstack ranges_obstack;
+};
+
+vn_walk_cb_data::~vn_walk_cb_data ()
+{
+  if (known_ranges)
+    {
+      splay_tree_delete (known_ranges);
+      obstack_free (&ranges_obstack, NULL);
+    }
+}
+
+/* pd_range splay-tree helpers.  */
+
+static int
+pd_range_compare (splay_tree_key offset1p, splay_tree_key offset2p)
+{
+  HOST_WIDE_INT offset1 = *(HOST_WIDE_INT *)offset1p;
+  HOST_WIDE_INT offset2 = *(HOST_WIDE_INT *)offset2p;
+  if (offset1 < offset2)
+    return -1;
+  else if (offset1 > offset2)
+    return 1;
+  return 0;
+}
+
+static void *
+pd_tree_alloc (int size, void *data_)
+{
+  vn_walk_cb_data *data = (vn_walk_cb_data *)data_;
+  return obstack_alloc (&data->ranges_obstack, size);
+}
+
+static void
+pd_tree_dealloc (void *, void *)
+{
+}
+
+/* Push PD to the vector of partial definitions returning a
+   value when we are ready to combine things with VUSE and MAXSIZEI,
+   NULL when we want to continue looking for partial defs or -1
+   on failure.  */
+
+void *
+vn_walk_cb_data::push_partial_def (const pd_data &pd, tree vuse,
+                                  HOST_WIDE_INT maxsizei)
+{
+  if (partial_defs.is_empty ())
+    {
+      partial_defs.safe_push (pd);
+      first_range.offset = pd.offset;
+      first_range.size = pd.size;
+      first_vuse = vuse;
+      last_vuse_ptr = NULL;
+      /* Continue looking for partial defs.  */
+      return NULL;
+    }
+
+  if (!known_ranges)
+    {
+      /* ???  Optimize the case where the 2nd partial def completes things.  */
+      gcc_obstack_init (&ranges_obstack);
+      known_ranges = splay_tree_new_with_allocator (pd_range_compare, 0, 0,
+                                                   pd_tree_alloc,
+                                                   pd_tree_dealloc, this);
+      splay_tree_insert (known_ranges,
+                        (splay_tree_key)&first_range.offset,
+                        (splay_tree_value)&first_range);
+    }
+
+  pd_range newr = { pd.offset, pd.size };
+  splay_tree_node n;
+  pd_range *r;
+  /* Lookup the predecessor of offset + 1 and see if we need to merge.  */
+  HOST_WIDE_INT loffset = newr.offset + 1;
+  if ((n = splay_tree_predecessor (known_ranges, (splay_tree_key)&loffset))
+      && ((r = (pd_range *)n->value), true)
+      && ranges_known_overlap_p (r->offset, r->size + 1,
+                                newr.offset, newr.size))
+    {
+      /* Ignore partial defs already covered.  */
+      if (known_subrange_p (newr.offset, newr.size, r->offset, r->size))
+       return NULL;
+      r->size = MAX (r->offset + r->size, newr.offset + newr.size) - r->offset;
+    }
+  else
+    {
+      /* newr.offset wasn't covered yet, insert the range.  */
+      r = XOBNEW (&ranges_obstack, pd_range);
+      *r = newr;
+      splay_tree_insert (known_ranges, (splay_tree_key)&r->offset,
+                        (splay_tree_value)r);
+    }
+  /* Merge r which now contains newr and is a member of the splay tree with
+     adjacent overlapping ranges.  */
+  pd_range *rafter;
+  while ((n = splay_tree_successor (known_ranges, (splay_tree_key)&r->offset))
+        && ((rafter = (pd_range *)n->value), true)
+        && ranges_known_overlap_p (r->offset, r->size + 1,
+                                   rafter->offset, rafter->size))
+    {
+      r->size = MAX (r->offset + r->size,
+                    rafter->offset + rafter->size) - r->offset;
+      splay_tree_remove (known_ranges, (splay_tree_key)&rafter->offset);
+    }
+  partial_defs.safe_push (pd);
+
+  /* Now we have merged newr into the range tree.  When we have covered
+     [offseti, sizei] then the tree will contain exactly one node which has
+     the desired properties and it will be 'r'.  */
+  if (!known_subrange_p (0, maxsizei / BITS_PER_UNIT, r->offset, r->size))
+    /* Continue looking for partial defs.  */
+    return NULL;
+
+  /* Now simply native encode all partial defs in reverse order.  */
+  unsigned ndefs = partial_defs.length ();
+  /* We support up to 512-bit values (for V8DFmode).  */
+  unsigned char buffer[64];
+  int len;
+
+  while (!partial_defs.is_empty ())
+    {
+      pd_data pd = partial_defs.pop ();
+      if (TREE_CODE (pd.rhs) == CONSTRUCTOR)
+       /* Empty CONSTRUCTOR.  */
+       memset (buffer + MAX (0, pd.offset),
+               0, MIN ((HOST_WIDE_INT)sizeof (buffer) - MAX (0, pd.offset),
+                       pd.size + MIN (0, pd.offset)));
+      else
+       {
+         unsigned pad = 0;
+         if (BYTES_BIG_ENDIAN
+             && is_a <scalar_mode> (TYPE_MODE (TREE_TYPE (pd.rhs))))
+           {
+             /* On big-endian the padding is at the 'front' so just skip
+                the initial bytes.  */
+             fixed_size_mode mode
+               = as_a <fixed_size_mode> (TYPE_MODE (TREE_TYPE (pd.rhs)));
+             pad = GET_MODE_SIZE (mode) - pd.size;
+           }
+         len = native_encode_expr (pd.rhs, buffer + MAX (0, pd.offset),
+                                   sizeof (buffer) - MAX (0, pd.offset),
+                                   MAX (0, -pd.offset) + pad);
+         if (len <= 0 || len < (pd.size - MAX (0, -pd.offset)))
+           {
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file, "Failed to encode %u "
+                        "partial definitions\n", ndefs);
+             return (void *)-1;
+           }
+       }
+    }
+
+  tree type = vr->type;
+  /* Make sure to interpret in a type that has a range covering the whole
+     access size.  */
+  if (INTEGRAL_TYPE_P (vr->type) && maxsizei != TYPE_PRECISION (vr->type))
+    type = build_nonstandard_integer_type (maxsizei, TYPE_UNSIGNED (type));
+  tree val = native_interpret_expr (type, buffer, maxsizei / BITS_PER_UNIT);
+  /* If we chop off bits because the types precision doesn't match the memory
+     access size this is ok when optimizing reads but not when called from
+     the DSE code during elimination.  */
+  if (val && type != vr->type)
+    {
+      if (! int_fits_type_p (val, vr->type))
+       val = NULL_TREE;
+      else
+       val = fold_convert (vr->type, val);
+    }
+
+  if (val)
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       fprintf (dump_file,
+                "Successfully combined %u partial definitions\n", ndefs);
+      return vn_reference_lookup_or_insert_for_pieces
+               (first_vuse, vr->set, vr->type, vr->operands, val);
+    }
+  else
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       fprintf (dump_file,
+                "Failed to interpret %u encoded partial definitions\n", ndefs);
+      return (void *)-1;
+    }
+}
+
 /* Callback for walk_non_aliased_vuses.  Adjusts the vn_reference_t VR_
    with the current VUSE and performs the expression lookup.  */
 
 static void *
-vn_reference_lookup_2 (ao_ref *op ATTRIBUTE_UNUSED, tree vuse,
-                      unsigned int cnt, void *vr_)
+vn_reference_lookup_2 (ao_ref *op ATTRIBUTE_UNUSED, tree vuse, void *data_)
 {
-  vn_reference_t vr = (vn_reference_t)vr_;
+  vn_walk_cb_data *data = (vn_walk_cb_data *)data_;
+  vn_reference_t vr = data->vr;
   vn_reference_s **slot;
   hashval_t hash;
 
-  /* This bounds the stmt walks we perform on reference lookups
-     to O(1) instead of O(N) where N is the number of dominating
-     stores.  */
-  if (cnt > (unsigned) PARAM_VALUE (PARAM_SCCVN_MAX_ALIAS_QUERIES_PER_ACCESS))
-    return (void *)-1;
+  /* If we have partial definitions recorded we have to go through
+     vn_reference_lookup_3.  */
+  if (!data->partial_defs.is_empty ())
+    return NULL;
 
-  if (last_vuse_ptr)
-    *last_vuse_ptr = vuse;
+  if (data->last_vuse_ptr)
+    *data->last_vuse_ptr = vuse;
 
   /* Fixup vuse and hash.  */
   if (vr->vuse)
@@ -1608,9 +1916,7 @@ vn_reference_lookup_2 (ao_ref *op ATTRIBUTE_UNUSED, tree vuse,
     vr->hashcode = vr->hashcode + SSA_NAME_VERSION (vr->vuse);
 
   hash = vr->hashcode;
-  slot = current_info->references->find_slot_with_hash (vr, hash, NO_INSERT);
-  if (!slot && current_info == optimistic_info)
-    slot = valid_info->references->find_slot_with_hash (vr, hash, NO_INSERT);
+  slot = valid_info->references->find_slot_with_hash (vr, hash, NO_INSERT);
   if (slot)
     return *slot;
 
@@ -1647,64 +1953,47 @@ vn_reference_lookup_or_insert_for_pieces (tree vuse,
                                     operands.copy (), value, value_id);
 }
 
-static vn_nary_op_t vn_nary_op_insert_stmt (gimple *stmt, tree result);
-
-/* Hook for maybe_push_res_to_seq, lookup the expression in the VN tables.  */
+/* Return a value-number for RCODE OPS... either by looking up an existing
+   value-number for the simplified result or by inserting the operation if
+   INSERT is true.  */
 
 static tree
-vn_lookup_simplify_result (gimple_match_op *res_op)
-{
-  if (!res_op->code.is_tree_code ())
-    return NULL_TREE;
-  tree *ops = res_op->ops;
-  unsigned int length = res_op->num_ops;
-  if (res_op->code == CONSTRUCTOR
-      /* ???  We're arriving here with SCCVNs view, decomposed CONSTRUCTOR
-         and GIMPLEs / match-and-simplifies, CONSTRUCTOR as GENERIC tree.  */
-      && TREE_CODE (res_op->ops[0]) == CONSTRUCTOR)
-    {
-      length = CONSTRUCTOR_NELTS (res_op->ops[0]);
-      ops = XALLOCAVEC (tree, length);
-      for (unsigned i = 0; i < length; ++i)
-       ops[i] = CONSTRUCTOR_ELT (res_op->ops[0], i)->value;
-    }
-  vn_nary_op_t vnresult = NULL;
-  return vn_nary_op_lookup_pieces (length, (tree_code) res_op->code,
-                                  res_op->type, ops, &vnresult);
-}
-
-/* Return a value-number for RCODE OPS... either by looking up an existing
-   value-number for the simplified result or by inserting the operation if
-   INSERT is true.  */
-
-static tree
-vn_nary_build_or_lookup_1 (gimple_match_op *res_op, bool insert)
+vn_nary_build_or_lookup_1 (gimple_match_op *res_op, bool insert)
 {
   tree result = NULL_TREE;
   /* We will be creating a value number for
        RCODE (OPS...).
      So first simplify and lookup this expression to see if it
      is already available.  */
-  mprts_hook = vn_lookup_simplify_result;
+  /* For simplification valueize.  */
+  unsigned i;
+  for (i = 0; i < res_op->num_ops; ++i)
+    if (TREE_CODE (res_op->ops[i]) == SSA_NAME)
+      {
+       tree tem = vn_valueize (res_op->ops[i]);
+       if (!tem)
+         break;
+       res_op->ops[i] = tem;
+      }
+  /* If valueization of an operand fails (it is not available), skip
+     simplification.  */
   bool res = false;
-  switch (TREE_CODE_LENGTH ((tree_code) res_op->code))
+  if (i == res_op->num_ops)
     {
-    case 1:
-      res = gimple_resimplify1 (NULL, res_op, vn_valueize);
-      break;
-    case 2:
-      res = gimple_resimplify2 (NULL, res_op, vn_valueize);
-      break;
-    case 3:
-      res = gimple_resimplify3 (NULL, res_op, vn_valueize);
-      break;
+      mprts_hook = vn_lookup_simplify_result;
+      res = res_op->resimplify (NULL, vn_valueize);
+      mprts_hook = NULL;
     }
-  mprts_hook = NULL;
   gimple *new_stmt = NULL;
   if (res
       && gimple_simplified_result_is_gimple_val (res_op))
-    /* The expression is already available.  */
-    result = res_op->ops[0];
+    {
+      /* The expression is already available.  */
+      result = res_op->ops[0];
+      /* Valueize it, simplification returns sth in AVAIL only.  */
+      if (TREE_CODE (result) == SSA_NAME)
+       result = SSA_VAL (result);
+    }
   else
     {
       tree val = vn_lookup_simplify_result (res_op);
@@ -1727,11 +2016,13 @@ vn_nary_build_or_lookup_1 (gimple_match_op *res_op, bool insert)
       /* The expression is not yet available, value-number lhs to
         the new SSA_NAME we created.  */
       /* Initialize value-number information properly.  */
-      VN_INFO_GET (result)->valnum = result;
-      VN_INFO (result)->value_id = get_next_value_id ();
+      vn_ssa_aux_t result_info = VN_INFO (result);
+      result_info->valnum = result;
+      result_info->value_id = get_next_value_id ();
+      result_info->visited = 1;
       gimple_seq_add_stmt_without_update (&VN_INFO (result)->expr,
                                          new_stmt);
-      VN_INFO (result)->needs_insertion = true;
+      result_info->needs_insertion = true;
       /* ???  PRE phi-translation inserts NARYs without corresponding
          SSA name result.  Re-use those but set their result according
         to the stmt we just built.  */
@@ -1739,8 +2030,8 @@ vn_nary_build_or_lookup_1 (gimple_match_op *res_op, bool insert)
       vn_nary_op_lookup_stmt (new_stmt, &nary);
       if (nary)
        {
-         gcc_assert (nary->result == NULL_TREE);
-         nary->result = gimple_assign_lhs (new_stmt);
+         gcc_assert (! nary->predicated_values && nary->u.result == NULL_TREE);
+         nary->u.result = gimple_assign_lhs (new_stmt);
        }
       /* As all "inserted" statements are singleton SCCs, insert
         to the valid table.  This is strictly needed to
@@ -1749,14 +2040,21 @@ vn_nary_build_or_lookup_1 (gimple_match_op *res_op, bool insert)
         optimistic table gets cleared after each iteration).
         We do not need to insert into the optimistic table, as
         lookups there will fall back to the valid table.  */
-      else if (current_info == optimistic_info)
+      else
        {
-         current_info = valid_info;
-         vn_nary_op_insert_stmt (new_stmt, result);
-         current_info = optimistic_info;
+         unsigned int length = vn_nary_length_from_stmt (new_stmt);
+         vn_nary_op_t vno1
+           = alloc_vn_nary_op_noinit (length, &vn_tables_insert_obstack);
+         vno1->value_id = result_info->value_id;
+         vno1->length = length;
+         vno1->predicated_values = 0;
+         vno1->u.result = result;
+         init_vn_nary_op_from_stmt (vno1, new_stmt);
+         vn_nary_op_insert_into (vno1, valid_info->nary, true);
+         /* Also do not link it into the undo chain.  */
+         last_inserted_nary = vno1->next;
+         vno1->next = (vn_nary_op_t)(void *)-1;
        }
-      else
-       vn_nary_op_insert_stmt (new_stmt, result);
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
          fprintf (dump_file, "Inserting name ");
@@ -1792,6 +2090,95 @@ vn_nary_simplify (vn_nary_op_t nary)
   return vn_nary_build_or_lookup_1 (&op, false);
 }
 
+/* Elimination engine.  */
+
+class eliminate_dom_walker : public dom_walker
+{
+public:
+  eliminate_dom_walker (cdi_direction, bitmap);
+  ~eliminate_dom_walker ();
+
+  virtual edge before_dom_children (basic_block);
+  virtual void after_dom_children (basic_block);
+
+  virtual tree eliminate_avail (basic_block, tree op);
+  virtual void eliminate_push_avail (basic_block, tree op);
+  tree eliminate_insert (basic_block, gimple_stmt_iterator *gsi, tree val);
+
+  void eliminate_stmt (basic_block, gimple_stmt_iterator *);
+
+  unsigned eliminate_cleanup (bool region_p = false);
+
+  bool do_pre;
+  unsigned int el_todo;
+  unsigned int eliminations;
+  unsigned int insertions;
+
+  /* SSA names that had their defs inserted by PRE if do_pre.  */
+  bitmap inserted_exprs;
+
+  /* Blocks with statements that have had their EH properties changed.  */
+  bitmap need_eh_cleanup;
+
+  /* Blocks with statements that have had their AB properties changed.  */
+  bitmap need_ab_cleanup;
+
+  /* Local state for the eliminate domwalk.  */
+  auto_vec<gimple *> to_remove;
+  auto_vec<gimple *> to_fixup;
+  auto_vec<tree> avail;
+  auto_vec<tree> avail_stack;
+};
+
+/* Adaptor to the elimination engine using RPO availability.  */
+
+class rpo_elim : public eliminate_dom_walker
+{
+public:
+  rpo_elim(basic_block entry_)
+    : eliminate_dom_walker (CDI_DOMINATORS, NULL), entry (entry_),
+      m_avail_freelist (NULL) {}
+
+  virtual tree eliminate_avail (basic_block, tree op);
+
+  virtual void eliminate_push_avail (basic_block, tree);
+
+  basic_block entry;
+  /* Freelist of avail entries which are allocated from the vn_ssa_aux
+     obstack.  */
+  vn_avail *m_avail_freelist;
+};
+
+/* Global RPO state for access from hooks.  */
+static rpo_elim *rpo_avail;
+basic_block vn_context_bb;
+
+/* Return true if BASE1 and BASE2 can be adjusted so they have the
+   same address and adjust *OFFSET1 and *OFFSET2 accordingly.
+   Otherwise return false.  */
+
+static bool
+adjust_offsets_for_equal_base_address (tree base1, poly_int64 *offset1,
+                                      tree base2, poly_int64 *offset2)
+{
+  poly_int64 soff;
+  if (TREE_CODE (base1) == MEM_REF
+      && TREE_CODE (base2) == MEM_REF)
+    {
+      if (mem_ref_offset (base1).to_shwi (&soff))
+       {
+         base1 = TREE_OPERAND (base1, 0);
+         *offset1 += soff * BITS_PER_UNIT;
+       }
+      if (mem_ref_offset (base2).to_shwi (&soff))
+       {
+         base2 = TREE_OPERAND (base2, 0);
+         *offset2 += soff * BITS_PER_UNIT;
+       }
+      return operand_equal_p (base1, base2, 0);
+    }
+  return operand_equal_p (base1, base2, OEP_ADDRESS_OF);
+}
 
 /* Callback for walk_non_aliased_vuses.  Tries to perform a lookup
    from the statement defining VUSE and if not successful tries to
@@ -1801,10 +2188,11 @@ vn_nary_simplify (vn_nary_op_t nary)
    *DISAMBIGUATE_ONLY is set to true.  */
 
 static void *
-vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
-                      bool *disambiguate_only)
+vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *data_,
+                      translate_flags *disambiguate_only)
 {
-  vn_reference_t vr = (vn_reference_t)vr_;
+  vn_walk_cb_data *data = (vn_walk_cb_data *)data_;
+  vn_reference_t vr = data->vr;
   gimple *def_stmt = SSA_NAME_DEF_STMT (vuse);
   tree base = ao_ref_base (ref);
   HOST_WIDE_INT offseti, maxsizei;
@@ -1813,18 +2201,6 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
   bool lhs_ref_ok = false;
   poly_int64 copy_size;
 
-  /* If the reference is based on a parameter that was determined as
-     pointing to readonly memory it doesn't change.  */
-  if (TREE_CODE (base) == MEM_REF
-      && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
-      && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0))
-      && bitmap_bit_p (const_parms,
-                      SSA_NAME_VERSION (TREE_OPERAND (base, 0))))
-    {
-      *disambiguate_only = true;
-      return NULL;
-    }
-
   /* First try to disambiguate after value-replacing in the definitions LHS.  */
   if (is_gimple_assign (def_stmt))
     {
@@ -1832,17 +2208,23 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       bool valueized_anything = false;
       /* Avoid re-allocation overhead.  */
       lhs_ops.truncate (0);
-      copy_reference_ops_from_ref (lhs, &lhs_ops);
-      lhs_ops = valueize_refs_1 (lhs_ops, &valueized_anything);
+      basic_block saved_rpo_bb = vn_context_bb;
+      vn_context_bb = gimple_bb (def_stmt);
+      if (*disambiguate_only <= TR_VALUEIZE_AND_DISAMBIGUATE)
+       {
+         copy_reference_ops_from_ref (lhs, &lhs_ops);
+         lhs_ops = valueize_refs_1 (lhs_ops, &valueized_anything, true);
+       }
+      vn_context_bb = saved_rpo_bb;
       if (valueized_anything)
        {
          lhs_ref_ok = ao_ref_init_from_vn_reference (&lhs_ref,
                                                      get_alias_set (lhs),
                                                      TREE_TYPE (lhs), lhs_ops);
          if (lhs_ref_ok
-             && !refs_may_alias_p_1 (ref, &lhs_ref, true))
+             && !refs_may_alias_p_1 (ref, &lhs_ref, data->tbaa_p))
            {
-             *disambiguate_only = true;
+             *disambiguate_only = TR_VALUEIZE_AND_DISAMBIGUATE;
              return NULL;
            }
        }
@@ -1852,40 +2234,69 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
          lhs_ref_ok = true;
        }
 
+      /* Besides valueizing the LHS we can also use access-path based
+         disambiguation on the original non-valueized ref.  */
+      if (!ref->ref
+         && lhs_ref_ok
+         && data->orig_ref.ref)
+       {
+         /* We want to use the non-valueized LHS for this, but avoid redundant
+            work.  */
+         ao_ref *lref = &lhs_ref;
+         ao_ref lref_alt;
+         if (valueized_anything)
+           {
+             ao_ref_init (&lref_alt, lhs);
+             lref = &lref_alt;
+           }
+         if (!refs_may_alias_p_1 (&data->orig_ref, lref, data->tbaa_p))
+           {
+             *disambiguate_only = (valueized_anything
+                                   ? TR_VALUEIZE_AND_DISAMBIGUATE
+                                   : TR_DISAMBIGUATE);
+             return NULL;
+           }
+       }
+
       /* If we reach a clobbering statement try to skip it and see if
          we find a VN result with exactly the same value as the
         possible clobber.  In this case we can ignore the clobber
-        and return the found value.
-        Note that we don't need to worry about partial overlapping
-        accesses as we then can use TBAA to disambiguate against the
-        clobbering statement when looking up a load (thus the
-        VN_WALKREWRITE guard).  */
-      if (vn_walk_kind == VN_WALKREWRITE
-         && is_gimple_reg_type (TREE_TYPE (lhs))
-         && types_compatible_p (TREE_TYPE (lhs), vr->type))
+        and return the found value.  */
+      if (is_gimple_reg_type (TREE_TYPE (lhs))
+         && types_compatible_p (TREE_TYPE (lhs), vr->type)
+         && ref->ref)
        {
-         tree *saved_last_vuse_ptr = last_vuse_ptr;
+         tree *saved_last_vuse_ptr = data->last_vuse_ptr;
          /* Do not update last_vuse_ptr in vn_reference_lookup_2.  */
-         last_vuse_ptr = NULL;
+         data->last_vuse_ptr = NULL;
          tree saved_vuse = vr->vuse;
          hashval_t saved_hashcode = vr->hashcode;
-         void *res = vn_reference_lookup_2 (ref,
-                                            gimple_vuse (def_stmt), 0, vr);
+         void *res = vn_reference_lookup_2 (ref, gimple_vuse (def_stmt), data);
          /* Need to restore vr->vuse and vr->hashcode.  */
          vr->vuse = saved_vuse;
          vr->hashcode = saved_hashcode;
-         last_vuse_ptr = saved_last_vuse_ptr;
+         data->last_vuse_ptr = saved_last_vuse_ptr;
          if (res && res != (void *)-1)
            {
              vn_reference_t vnresult = (vn_reference_t) res;
+             tree rhs = gimple_assign_rhs1 (def_stmt);
+             if (TREE_CODE (rhs) == SSA_NAME)
+               rhs = SSA_VAL (rhs);
              if (vnresult->result
-                 && operand_equal_p (vnresult->result,
-                                     gimple_assign_rhs1 (def_stmt), 0))
+                 && operand_equal_p (vnresult->result, rhs, 0)
+                 /* We have to honor our promise about union type punning
+                    and also support arbitrary overlaps with
+                    -fno-strict-aliasing.  So simply resort to alignment to
+                    rule out overlaps.  Do this check last because it is
+                    quite expensive compared to the hash-lookup above.  */
+                 && multiple_p (get_object_alignment (ref->ref), ref->size)
+                 && multiple_p (get_object_alignment (lhs), ref->size))
                return res;
            }
        }
     }
-  else if (gimple_call_builtin_p (def_stmt, BUILT_IN_NORMAL)
+  else if (*disambiguate_only <= TR_VALUEIZE_AND_DISAMBIGUATE
+          && gimple_call_builtin_p (def_stmt, BUILT_IN_NORMAL)
           && gimple_call_num_args (def_stmt) <= 4)
     {
       /* For builtin calls valueize its arguments and call the
@@ -1914,13 +2325,15 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
            gimple_call_set_arg (def_stmt, i, oldargs[i]);
          if (!res)
            {
-             *disambiguate_only = true;
+             *disambiguate_only = TR_VALUEIZE_AND_DISAMBIGUATE;
              return NULL;
            }
        }
     }
 
-  if (*disambiguate_only)
+  /* If we are looking for redundant stores do not create new hashtable
+     entries from aliasing defs with made up alias-sets.  */
+  if (*disambiguate_only > TR_TRANSLATE || !data->tbaa_p)
     return (void *)-1;
 
   /* If we cannot constrain the size of the reference we cannot
@@ -2006,8 +2419,10 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       else
        return (void *)-1;
       tree len = gimple_call_arg (def_stmt, 2);
-      if (known_subrange_p (offset, maxsize, offset2,
-                           wi::to_poly_offset (len) << LOG2_BITS_PER_UNIT))
+      HOST_WIDE_INT leni, offset2i, offseti;
+      if (data->partial_defs.is_empty ()
+         && known_subrange_p (offset, maxsize, offset2,
+                              wi::to_poly_offset (len) << LOG2_BITS_PER_UNIT))
        {
          tree val;
          if (integer_zerop (gimple_call_arg (def_stmt, 1)))
@@ -2036,6 +2451,20 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
          return vn_reference_lookup_or_insert_for_pieces
                   (vuse, vr->set, vr->type, vr->operands, val);
        }
+      /* For now handle clearing memory with partial defs.  */
+      else if (known_eq (ref->size, maxsize)
+              && integer_zerop (gimple_call_arg (def_stmt, 1))
+              && tree_to_poly_int64 (len).is_constant (&leni)
+              && offset.is_constant (&offseti)
+              && offset2.is_constant (&offset2i)
+              && maxsize.is_constant (&maxsizei))
+       {
+         pd_data pd;
+         pd.rhs = build_constructor (NULL_TREE, NULL);
+         pd.offset = (offset2i - offseti) / BITS_PER_UNIT;
+         pd.size = leni;
+         return data->push_partial_def (pd, vuse, maxsizei);
+       }
     }
 
   /* 2) Assignment from an empty CONSTRUCTOR.  */
@@ -2044,18 +2473,50 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
           && gimple_assign_rhs_code (def_stmt) == CONSTRUCTOR
           && CONSTRUCTOR_NELTS (gimple_assign_rhs1 (def_stmt)) == 0)
     {
+      tree lhs = gimple_assign_lhs (def_stmt);
       tree base2;
       poly_int64 offset2, size2, maxsize2;
+      HOST_WIDE_INT offset2i, size2i;
       bool reverse;
-      base2 = get_ref_base_and_extent (gimple_assign_lhs (def_stmt),
-                                      &offset2, &size2, &maxsize2, &reverse);
+      if (lhs_ref_ok)
+       {
+         base2 = ao_ref_base (&lhs_ref);
+         offset2 = lhs_ref.offset;
+         size2 = lhs_ref.size;
+         maxsize2 = lhs_ref.max_size;
+         reverse = reverse_storage_order_for_component_p (lhs);
+       }
+      else
+       base2 = get_ref_base_and_extent (lhs,
+                                        &offset2, &size2, &maxsize2, &reverse);
       if (known_size_p (maxsize2)
-         && operand_equal_p (base, base2, 0)
-         && known_subrange_p (offset, maxsize, offset2, size2))
+         && known_eq (maxsize2, size2)
+         && adjust_offsets_for_equal_base_address (base, &offset,
+                                                   base2, &offset2))
        {
-         tree val = build_zero_cst (vr->type);
-         return vn_reference_lookup_or_insert_for_pieces
-                  (vuse, vr->set, vr->type, vr->operands, val);
+         if (data->partial_defs.is_empty ()
+             && known_subrange_p (offset, maxsize, offset2, size2))
+           {
+             tree val = build_zero_cst (vr->type);
+             return vn_reference_lookup_or_insert_for_pieces
+                 (vuse, vr->set, vr->type, vr->operands, val);
+           }
+         else if (known_eq (ref->size, maxsize)
+                  && maxsize.is_constant (&maxsizei)
+                  && maxsizei % BITS_PER_UNIT == 0
+                  && offset.is_constant (&offseti)
+                  && offseti % BITS_PER_UNIT == 0
+                  && offset2.is_constant (&offset2i)
+                  && offset2i % BITS_PER_UNIT == 0
+                  && size2.is_constant (&size2i)
+                  && size2i % BITS_PER_UNIT == 0)
+           {
+             pd_data pd;
+             pd.rhs = gimple_assign_rhs1 (def_stmt);
+             pd.offset = (offset2i - offseti) / BITS_PER_UNIT;
+             pd.size = size2i / BITS_PER_UNIT;
+             return data->push_partial_def (pd, vuse, maxsizei);
+           }
        }
     }
 
@@ -2076,55 +2537,96 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
               || (TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME
                   && is_gimple_min_invariant (SSA_VAL (gimple_assign_rhs1 (def_stmt))))))
     {
+      tree lhs = gimple_assign_lhs (def_stmt);
       tree base2;
-      HOST_WIDE_INT offset2, size2;
+      poly_int64 offset2, size2, maxsize2;
+      HOST_WIDE_INT offset2i, size2i;
       bool reverse;
-      base2 = get_ref_base_and_extent_hwi (gimple_assign_lhs (def_stmt),
-                                          &offset2, &size2, &reverse);
+      if (lhs_ref_ok)
+       {
+         base2 = ao_ref_base (&lhs_ref);
+         offset2 = lhs_ref.offset;
+         size2 = lhs_ref.size;
+         maxsize2 = lhs_ref.max_size;
+         reverse = reverse_storage_order_for_component_p (lhs);
+       }
+      else
+       base2 = get_ref_base_and_extent (lhs,
+                                        &offset2, &size2, &maxsize2, &reverse);
       if (base2
          && !reverse
-         && size2 % BITS_PER_UNIT == 0
-         && offset2 % BITS_PER_UNIT == 0
-         && operand_equal_p (base, base2, 0)
-         && known_subrange_p (offseti, maxsizei, offset2, size2))
+         && known_eq (maxsize2, size2)
+         && multiple_p (size2, BITS_PER_UNIT)
+         && multiple_p (offset2, BITS_PER_UNIT)
+         && adjust_offsets_for_equal_base_address (base, &offset,
+                                                   base2, &offset2)
+         && offset.is_constant (&offseti)
+         && offset2.is_constant (&offset2i)
+         && size2.is_constant (&size2i))
        {
-         /* We support up to 512-bit values (for V8DFmode).  */
-         unsigned char buffer[64];
-         int len;
-
-         tree rhs = gimple_assign_rhs1 (def_stmt);
-         if (TREE_CODE (rhs) == SSA_NAME)
-           rhs = SSA_VAL (rhs);
-         len = native_encode_expr (gimple_assign_rhs1 (def_stmt),
-                                   buffer, sizeof (buffer),
-                                   (offseti - offset2) / BITS_PER_UNIT);
-         if (len > 0 && len * BITS_PER_UNIT >= maxsizei)
+         if (data->partial_defs.is_empty ()
+             && known_subrange_p (offseti, maxsizei, offset2, size2))
            {
-             tree type = vr->type;
-             /* Make sure to interpret in a type that has a range
-                covering the whole access size.  */
-             if (INTEGRAL_TYPE_P (vr->type)
-                 && maxsizei != TYPE_PRECISION (vr->type))
-               type = build_nonstandard_integer_type (maxsizei,
-                                                      TYPE_UNSIGNED (type));
-             tree val = native_interpret_expr (type, buffer,
-                                               maxsizei / BITS_PER_UNIT);
-             /* If we chop off bits because the types precision doesn't
-                match the memory access size this is ok when optimizing
-                reads but not when called from the DSE code during
-                elimination.  */
-             if (val
-                 && type != vr->type)
+             /* We support up to 512-bit values (for V8DFmode).  */
+             unsigned char buffer[64];
+             int len;
+
+             tree rhs = gimple_assign_rhs1 (def_stmt);
+             if (TREE_CODE (rhs) == SSA_NAME)
+               rhs = SSA_VAL (rhs);
+             unsigned pad = 0;
+             if (BYTES_BIG_ENDIAN
+                 && is_a <scalar_mode> (TYPE_MODE (TREE_TYPE (rhs))))
                {
-                 if (! int_fits_type_p (val, vr->type))
-                   val = NULL_TREE;
-                 else
-                   val = fold_convert (vr->type, val);
+                 /* On big-endian the padding is at the 'front' so
+                    just skip the initial bytes.  */
+                 fixed_size_mode mode
+                   = as_a <fixed_size_mode> (TYPE_MODE (TREE_TYPE (rhs)));
+                 pad = GET_MODE_SIZE (mode) - size2i / BITS_PER_UNIT;
                }
+             len = native_encode_expr (rhs,
+                                       buffer, sizeof (buffer),
+                                       ((offseti - offset2i) / BITS_PER_UNIT
+                                        + pad));
+             if (len > 0 && len * BITS_PER_UNIT >= maxsizei)
+               {
+                 tree type = vr->type;
+                 /* Make sure to interpret in a type that has a range
+                    covering the whole access size.  */
+                 if (INTEGRAL_TYPE_P (vr->type)
+                     && maxsizei != TYPE_PRECISION (vr->type))
+                   type = build_nonstandard_integer_type (maxsizei,
+                                                          TYPE_UNSIGNED (type));
+                 tree val = native_interpret_expr (type, buffer,
+                                                   maxsizei / BITS_PER_UNIT);
+                 /* If we chop off bits because the types precision doesn't
+                    match the memory access size this is ok when optimizing
+                    reads but not when called from the DSE code during
+                    elimination.  */
+                 if (val
+                     && type != vr->type)
+                   {
+                     if (! int_fits_type_p (val, vr->type))
+                       val = NULL_TREE;
+                     else
+                       val = fold_convert (vr->type, val);
+                   }
 
-             if (val)
-               return vn_reference_lookup_or_insert_for_pieces
-                        (vuse, vr->set, vr->type, vr->operands, val);
+                 if (val)
+                   return vn_reference_lookup_or_insert_for_pieces
+                       (vuse, vr->set, vr->type, vr->operands, val);
+               }
+           }
+         else if (ranges_known_overlap_p (offseti, maxsizei, offset2i, size2i))
+           {
+             pd_data pd;
+             tree rhs = gimple_assign_rhs1 (def_stmt);
+             if (TREE_CODE (rhs) == SSA_NAME)
+               rhs = SSA_VAL (rhs);
+             pd.rhs = rhs;
+             pd.offset = (offset2i - offseti) / BITS_PER_UNIT;
+             pd.size = size2i / BITS_PER_UNIT;
+             return data->push_partial_def (pd, vuse, maxsizei);
            }
        }
     }
@@ -2135,18 +2637,34 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
           && is_gimple_reg_type (vr->type)
           && !contains_storage_order_barrier_p (vr->operands)
           && gimple_assign_single_p (def_stmt)
-          && TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME)
+          && TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME
+          /* A subset of partial defs from non-constants can be handled
+             by for example inserting a CONSTRUCTOR, a COMPLEX_EXPR or
+             even a (series of) BIT_INSERT_EXPR hoping for simplifications
+             downstream, not so much for actually doing the insertion.  */
+          && data->partial_defs.is_empty ())
     {
+      tree lhs = gimple_assign_lhs (def_stmt);
       tree base2;
       poly_int64 offset2, size2, maxsize2;
       bool reverse;
-      base2 = get_ref_base_and_extent (gimple_assign_lhs (def_stmt),
-                                      &offset2, &size2, &maxsize2,
-                                      &reverse);
+      if (lhs_ref_ok)
+       {
+         base2 = ao_ref_base (&lhs_ref);
+         offset2 = lhs_ref.offset;
+         size2 = lhs_ref.size;
+         maxsize2 = lhs_ref.max_size;
+         reverse = reverse_storage_order_for_component_p (lhs);
+       }
+      else
+       base2 = get_ref_base_and_extent (lhs,
+                                        &offset2, &size2, &maxsize2, &reverse);
+      tree def_rhs = gimple_assign_rhs1 (def_stmt);
       if (!reverse
          && known_size_p (maxsize2)
          && known_eq (maxsize2, size2)
-         && operand_equal_p (base, base2, 0)
+         && adjust_offsets_for_equal_base_address (base, &offset,
+                                                   base2, &offset2)
          && known_subrange_p (offset, maxsize, offset2, size2)
          /* ???  We can't handle bitfield precision extracts without
             either using an alternate type for the BIT_FIELD_REF and
@@ -2154,11 +2672,13 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
             according to endianness.  */
          && (! INTEGRAL_TYPE_P (vr->type)
              || known_eq (ref->size, TYPE_PRECISION (vr->type)))
-         && multiple_p (ref->size, BITS_PER_UNIT))
+         && multiple_p (ref->size, BITS_PER_UNIT)
+         && (! INTEGRAL_TYPE_P (TREE_TYPE (def_rhs))
+             || type_has_mode_precision_p (TREE_TYPE (def_rhs))))
        {
          gimple_match_op op (gimple_match_cond::UNCOND,
                              BIT_FIELD_REF, vr->type,
-                             SSA_VAL (gimple_assign_rhs1 (def_stmt)),
+                             vn_valueize (def_rhs),
                              bitsize_int (ref->size),
                              bitsize_int (offset - offset2));
          tree val = vn_nary_build_or_lookup (&op);
@@ -2175,7 +2695,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
 
   /* 5) For aggregate copies translate the reference through them if
      the copy kills ref.  */
-  else if (vn_walk_kind == VN_WALKREWRITE
+  else if (data->vn_walk_kind == VN_WALKREWRITE
           && gimple_assign_single_p (def_stmt)
           && (DECL_P (gimple_assign_rhs1 (def_stmt))
               || TREE_CODE (gimple_assign_rhs1 (def_stmt)) == MEM_REF
@@ -2283,8 +2803,30 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       /* Try folding the new reference to a constant.  */
       tree val = fully_constant_vn_reference_p (vr);
       if (val)
-       return vn_reference_lookup_or_insert_for_pieces
-                (vuse, vr->set, vr->type, vr->operands, val);
+       {
+         if (data->partial_defs.is_empty ())
+           return vn_reference_lookup_or_insert_for_pieces
+               (vuse, vr->set, vr->type, vr->operands, val);
+         /* This is the only interesting case for partial-def handling
+            coming from targets that like to gimplify init-ctors as
+            aggregate copies from constant data like aarch64 for
+            PR83518.  */
+         if (maxsize.is_constant (&maxsizei)
+             && known_eq (ref->size, maxsize))
+           {
+             pd_data pd;
+             pd.rhs = val;
+             pd.offset = 0;
+             pd.size = maxsizei / BITS_PER_UNIT;
+             return data->push_partial_def (pd, vuse, maxsizei);
+           }
+       }
+
+      /* Continuing with partial defs isn't easily possible here, we
+         have to find a full def from further lookups from here.  Probably
+        not worth the special-casing everywhere.  */
+      if (!data->partial_defs.is_empty ())
+       return (void *)-1;
 
       /* Adjust *ref from the new operands.  */
       if (!ao_ref_init_from_vn_reference (&r, vr->set, vr->type, vr->operands))
@@ -2295,7 +2837,10 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       *ref = r;
 
       /* Do not update last seen VUSE after translating.  */
-      last_vuse_ptr = NULL;
+      data->last_vuse_ptr = NULL;
+      /* Invalidate the original access path since it now contains
+         the wrong base.  */
+      data->orig_ref.ref = NULL_TREE;
 
       /* Keep looking for the adjusted *REF / VR pair.  */
       return NULL;
@@ -2303,7 +2848,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
 
   /* 6) For memcpy copies translate the reference through them if
      the copy kills ref.  */
-  else if (vn_walk_kind == VN_WALKREWRITE
+  else if (data->vn_walk_kind == VN_WALKREWRITE
           && is_gimple_reg_type (vr->type)
           /* ???  Handle BCOPY as well.  */
           && (gimple_call_builtin_p (def_stmt, BUILT_IN_MEMCPY)
@@ -2313,7 +2858,9 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
               || TREE_CODE (gimple_call_arg (def_stmt, 0)) == SSA_NAME)
           && (TREE_CODE (gimple_call_arg (def_stmt, 1)) == ADDR_EXPR
               || TREE_CODE (gimple_call_arg (def_stmt, 1)) == SSA_NAME)
-          && poly_int_tree_p (gimple_call_arg (def_stmt, 2), &copy_size))
+          && poly_int_tree_p (gimple_call_arg (def_stmt, 2), &copy_size)
+          /* Handling this is more complicated, give up for now.  */
+          && data->partial_defs.is_empty ())
     {
       tree lhs, rhs;
       ao_ref r;
@@ -2333,7 +2880,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       lhs_offset = 0;
       if (TREE_CODE (lhs) == SSA_NAME)
        {
-         lhs = SSA_VAL (lhs);
+         lhs = vn_valueize (lhs);
          if (TREE_CODE (lhs) == SSA_NAME)
            {
              gimple *def_stmt = SSA_NAME_DEF_STMT (lhs);
@@ -2353,7 +2900,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
            {
              lhs = TREE_OPERAND (tem, 0);
              if (TREE_CODE (lhs) == SSA_NAME)
-               lhs = SSA_VAL (lhs);
+               lhs = vn_valueize (lhs);
              lhs_offset += mem_offset;
            }
          else if (DECL_P (tem))
@@ -2369,7 +2916,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       rhs = gimple_call_arg (def_stmt, 1);
       rhs_offset = 0;
       if (TREE_CODE (rhs) == SSA_NAME)
-       rhs = SSA_VAL (rhs);
+       rhs = vn_valueize (rhs);
       if (TREE_CODE (rhs) == ADDR_EXPR)
        {
          tree tem = get_addr_base_and_unit_offset (TREE_OPERAND (rhs, 0),
@@ -2388,8 +2935,9 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
          else
            return (void *)-1;
        }
-      if (TREE_CODE (rhs) != SSA_NAME
-         && TREE_CODE (rhs) != ADDR_EXPR)
+      if (TREE_CODE (rhs) == SSA_NAME)
+       rhs = SSA_VAL (rhs);
+      else if (TREE_CODE (rhs) != ADDR_EXPR)
        return (void *)-1;
 
       /* The bases of the destination and the references have to agree.  */
@@ -2453,7 +3001,10 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       *ref = r;
 
       /* Do not update last seen VUSE after translating.  */
-      last_vuse_ptr = NULL;
+      data->last_vuse_ptr = NULL;
+      /* Invalidate the original access path since it now contains
+         the wrong base.  */
+      data->orig_ref.ref = NULL_TREE;
 
       /* Keep looking for the adjusted *REF / VR pair.  */
       return NULL;
@@ -2513,13 +3064,14 @@ vn_reference_lookup_pieces (tree vuse, alias_set_type set, tree type,
       && vr1.vuse)
     {
       ao_ref r;
-      vn_walk_kind = kind;
+      unsigned limit = PARAM_VALUE (PARAM_SCCVN_MAX_ALIAS_QUERIES_PER_ACCESS);
+      vn_walk_cb_data data (&vr1, NULL_TREE, NULL, kind, true);
       if (ao_ref_init_from_vn_reference (&r, set, type, vr1.operands))
        *vnresult =
-         (vn_reference_t)walk_non_aliased_vuses (&r, vr1.vuse,
+         (vn_reference_t)walk_non_aliased_vuses (&r, vr1.vuse, true,
                                                  vn_reference_lookup_2,
                                                  vn_reference_lookup_3,
-                                                 vuse_ssa_val, &vr1);
+                                                 vuse_valueize, limit, &data);
       gcc_checking_assert (vr1.operands == shared_lookup_references);
     }
 
@@ -2534,11 +3086,12 @@ vn_reference_lookup_pieces (tree vuse, alias_set_type set, tree type,
    not exist in the hash table or if the result field of the structure
    was NULL..  VNRESULT will be filled in with the vn_reference_t
    stored in the hashtable if one exists.  When TBAA_P is false assume
-   we are looking up a store and treat it as having alias-set zero.  */
+   we are looking up a store and treat it as having alias-set zero.
+   *LAST_VUSE_PTR will be updated with the VUSE the value lookup succeeded.  */
 
 tree
 vn_reference_lookup (tree op, tree vuse, vn_lookup_kind kind,
-                    vn_reference_t *vnresult, bool tbaa_p)
+                    vn_reference_t *vnresult, bool tbaa_p, tree *last_vuse_ptr)
 {
   vec<vn_reference_op_s> operands;
   struct vn_reference_s vr1;
@@ -2552,7 +3105,7 @@ vn_reference_lookup (tree op, tree vuse, vn_lookup_kind kind,
   vr1.operands = operands
     = valueize_shared_reference_ops_from_ref (op, &valuezied_anything);
   vr1.type = TREE_TYPE (op);
-  vr1.set = tbaa_p ? get_alias_set (op) : 0;
+  vr1.set = get_alias_set (op);
   vr1.hashcode = vn_reference_compute_hash (&vr1);
   if ((cst = fully_constant_vn_reference_p (&vr1)))
     return cst;
@@ -2562,20 +3115,20 @@ vn_reference_lookup (tree op, tree vuse, vn_lookup_kind kind,
     {
       vn_reference_t wvnresult;
       ao_ref r;
+      unsigned limit = PARAM_VALUE (PARAM_SCCVN_MAX_ALIAS_QUERIES_PER_ACCESS);
       /* Make sure to use a valueized reference if we valueized anything.
          Otherwise preserve the full reference for advanced TBAA.  */
       if (!valuezied_anything
          || !ao_ref_init_from_vn_reference (&r, vr1.set, vr1.type,
                                             vr1.operands))
        ao_ref_init (&r, op);
-      if (! tbaa_p)
-       r.ref_alias_set = r.base_alias_set = 0;
-      vn_walk_kind = kind;
+      vn_walk_cb_data data (&vr1, r.ref ? NULL_TREE : op,
+                           last_vuse_ptr, kind, tbaa_p);
       wvnresult =
-       (vn_reference_t)walk_non_aliased_vuses (&r, vr1.vuse,
+       (vn_reference_t)walk_non_aliased_vuses (&r, vr1.vuse, tbaa_p,
                                                vn_reference_lookup_2,
                                                vn_reference_lookup_3,
-                                               vuse_ssa_val, &vr1);
+                                               vuse_valueize, limit, &data);
       gcc_checking_assert (vr1.operands == shared_lookup_references);
       if (wvnresult)
        {
@@ -2610,22 +3163,21 @@ vn_reference_lookup_call (gcall *call, vn_reference_t *vnresult,
   vn_reference_lookup_1 (vr, vnresult);
 }
 
-/* Insert OP into the current hash table with a value number of
-   RESULT, and return the resulting reference structure we created.  */
+/* Insert OP into the current hash table with a value number of RESULT.  */
 
-static vn_reference_t
+static void 
 vn_reference_insert (tree op, tree result, tree vuse, tree vdef)
 {
   vn_reference_s **slot;
   vn_reference_t vr1;
   bool tem;
 
-  vr1 = current_info->references_pool->allocate ();
+  vr1 = XOBNEW (&vn_tables_obstack, vn_reference_s);
   if (TREE_CODE (result) == SSA_NAME)
     vr1->value_id = VN_INFO (result)->value_id;
   else
     vr1->value_id = get_or_alloc_constant_value_id (result);
-  vr1->vuse = vuse ? SSA_VAL (vuse) : NULL_TREE;
+  vr1->vuse = vuse_ssa_val (vuse);
   vr1->operands = valueize_shared_reference_ops_from_ref (op, &tem).copy ();
   vr1->type = TREE_TYPE (op);
   vr1->set = get_alias_set (op);
@@ -2633,23 +3185,36 @@ vn_reference_insert (tree op, tree result, tree vuse, tree vdef)
   vr1->result = TREE_CODE (result) == SSA_NAME ? SSA_VAL (result) : result;
   vr1->result_vdef = vdef;
 
-  slot = current_info->references->find_slot_with_hash (vr1, vr1->hashcode,
-                                                       INSERT);
+  slot = valid_info->references->find_slot_with_hash (vr1, vr1->hashcode,
+                                                     INSERT);
 
-  /* Because we lookup stores using vuses, and value number failures
-     using the vdefs (see visit_reference_op_store for how and why),
-     it's possible that on failure we may try to insert an already
-     inserted store.  This is not wrong, there is no ssa name for a
-     store that we could use as a differentiator anyway.  Thus, unlike
-     the other lookup functions, you cannot gcc_assert (!*slot)
-     here.  */
-
-  /* But free the old slot in case of a collision.  */
+  /* Because IL walking on reference lookup can end up visiting
+     a def that is only to be visited later in iteration order
+     when we are about to make an irreducible region reducible
+     the def can be effectively processed and its ref being inserted
+     by vn_reference_lookup_3 already.  So we cannot assert (!*slot)
+     but save a lookup if we deal with already inserted refs here.  */
   if (*slot)
-    free_reference (*slot);
+    {
+      /* We cannot assert that we have the same value either because
+         when disentangling an irreducible region we may end up visiting
+        a use before the corresponding def.  That's a missed optimization
+        only though.  See gcc.dg/tree-ssa/pr87126.c for example.  */
+      if (dump_file && (dump_flags & TDF_DETAILS)
+         && !operand_equal_p ((*slot)->result, vr1->result, 0))
+       {
+         fprintf (dump_file, "Keeping old value ");
+         print_generic_expr (dump_file, (*slot)->result);
+         fprintf (dump_file, " because of collision\n");
+       }
+      free_reference (vr1);
+      obstack_free (&vn_tables_obstack, vr1);
+      return;
+    }
 
   *slot = vr1;
-  return vr1;
+  vr1->next = last_inserted_ref;
+  last_inserted_ref = vr1;
 }
 
 /* Insert a reference by it's pieces into the current hash table with
@@ -2665,9 +3230,9 @@ vn_reference_insert_pieces (tree vuse, alias_set_type set, tree type,
   vn_reference_s **slot;
   vn_reference_t vr1;
 
-  vr1 = current_info->references_pool->allocate ();
+  vr1 = XOBNEW (&vn_tables_obstack, vn_reference_s);
   vr1->value_id = value_id;
-  vr1->vuse = vuse ? SSA_VAL (vuse) : NULL_TREE;
+  vr1->vuse = vuse_ssa_val (vuse);
   vr1->operands = valueize_refs (operands);
   vr1->type = type;
   vr1->set = set;
@@ -2676,17 +3241,17 @@ vn_reference_insert_pieces (tree vuse, alias_set_type set, tree type,
     result = SSA_VAL (result);
   vr1->result = result;
 
-  slot = current_info->references->find_slot_with_hash (vr1, vr1->hashcode,
-                                                       INSERT);
+  slot = valid_info->references->find_slot_with_hash (vr1, vr1->hashcode,
+                                                     INSERT);
 
   /* At this point we should have all the things inserted that we have
      seen before, and we should never try inserting something that
      already exists.  */
   gcc_assert (!*slot);
-  if (*slot)
-    free_reference (*slot);
 
   *slot = vr1;
+  vr1->next = last_inserted_ref;
+  last_inserted_ref = vr1;
   return vr1;
 }
 
@@ -2767,20 +3332,6 @@ init_vn_nary_op_from_pieces (vn_nary_op_t vno, unsigned int length,
   memcpy (&vno->op[0], ops, sizeof (tree) * length);
 }
 
-/* Initialize VNO from OP.  */
-
-static void
-init_vn_nary_op_from_op (vn_nary_op_t vno, tree op)
-{
-  unsigned i;
-
-  vno->opcode = TREE_CODE (op);
-  vno->length = TREE_CODE_LENGTH (TREE_CODE (op));
-  vno->type = TREE_TYPE (op);
-  for (i = 0; i < vno->length; ++i)
-    vno->op[i] = TREE_OPERAND (op, i);
-}
-
 /* Return the number of operands for a vn_nary ops structure from STMT.  */
 
 static unsigned int
@@ -2858,16 +3409,12 @@ vn_nary_op_lookup_1 (vn_nary_op_t vno, vn_nary_op_t *vnresult)
     *vnresult = NULL;
 
   vno->hashcode = vn_nary_op_compute_hash (vno);
-  slot = current_info->nary->find_slot_with_hash (vno, vno->hashcode,
-                                                 NO_INSERT);
-  if (!slot && current_info == optimistic_info)
-    slot = valid_info->nary->find_slot_with_hash (vno, vno->hashcode,
-                                                 NO_INSERT);
+  slot = valid_info->nary->find_slot_with_hash (vno, vno->hashcode, NO_INSERT);
   if (!slot)
     return NULL_TREE;
   if (vnresult)
     *vnresult = *slot;
-  return (*slot)->result;
+  return (*slot)->predicated_values ? NULL_TREE : (*slot)->u.result;
 }
 
 /* Lookup a n-ary operation by its pieces and return the resulting value
@@ -2886,22 +3433,6 @@ vn_nary_op_lookup_pieces (unsigned int length, enum tree_code code,
   return vn_nary_op_lookup_1 (vno1, vnresult);
 }
 
-/* Lookup OP in the current hash table, and return the resulting value
-   number if it exists in the hash table.  Return NULL_TREE if it does
-   not exist in the hash table or if the result field of the operation
-   is NULL. VNRESULT will contain the vn_nary_op_t from the hashtable
-   if it exists.  */
-
-tree
-vn_nary_op_lookup (tree op, vn_nary_op_t *vnresult)
-{
-  vn_nary_op_t vno1
-    = XALLOCAVAR (struct vn_nary_op_s,
-                 sizeof_vn_nary_op (TREE_CODE_LENGTH (TREE_CODE (op))));
-  init_vn_nary_op_from_op (vno1, op);
-  return vn_nary_op_lookup_1 (vno1, vnresult);
-}
-
 /* Lookup the rhs of STMT in the current hash table, and return the resulting
    value number if it exists in the hash table.  Return NULL_TREE if
    it does not exist in the hash table.  VNRESULT will contain the
@@ -2931,12 +3462,12 @@ alloc_vn_nary_op_noinit (unsigned int length, struct obstack *stack)
 static vn_nary_op_t
 alloc_vn_nary_op (unsigned int length, tree result, unsigned int value_id)
 {
-  vn_nary_op_t vno1 = alloc_vn_nary_op_noinit (length,
-                                              &current_info->nary_obstack);
+  vn_nary_op_t vno1 = alloc_vn_nary_op_noinit (length, &vn_tables_obstack);
 
   vno1->value_id = value_id;
   vno1->length = length;
-  vno1->result = result;
+  vno1->predicated_values = 0;
+  vno1->u.result = result;
 
   return vno1;
 }
@@ -2951,21 +3482,129 @@ vn_nary_op_insert_into (vn_nary_op_t vno, vn_nary_op_table_type *table,
   vn_nary_op_s **slot;
 
   if (compute_hash)
-    vno->hashcode = vn_nary_op_compute_hash (vno);
+    {
+      vno->hashcode = vn_nary_op_compute_hash (vno);
+      gcc_assert (! vno->predicated_values
+                 || (! vno->u.values->next
+                     && vno->u.values->n == 1));
+    }
 
   slot = table->find_slot_with_hash (vno, vno->hashcode, INSERT);
-  /* While we do not want to insert things twice it's awkward to
-     avoid it in the case where visit_nary_op pattern-matches stuff
-     and ends up simplifying the replacement to itself.  We then
-     get two inserts, one from visit_nary_op and one from
-     vn_nary_build_or_lookup.
-     So allow inserts with the same value number.  */
-  if (*slot && (*slot)->result == vno->result)
-    return *slot;
+  vno->unwind_to = *slot;
+  if (*slot)
+    {
+      /* Prefer non-predicated values.
+         ???  Only if those are constant, otherwise, with constant predicated
+        value, turn them into predicated values with entry-block validity
+        (???  but we always find the first valid result currently).  */
+      if ((*slot)->predicated_values
+         && ! vno->predicated_values)
+       {
+         /* ???  We cannot remove *slot from the unwind stack list.
+            For the moment we deal with this by skipping not found
+            entries but this isn't ideal ...  */
+         *slot = vno;
+         /* ???  Maintain a stack of states we can unwind in
+            vn_nary_op_s?  But how far do we unwind?  In reality
+            we need to push change records somewhere...  Or not
+            unwind vn_nary_op_s and linking them but instead
+            unwind the results "list", linking that, which also
+            doesn't move on hashtable resize.  */
+         /* We can also have a ->unwind_to recording *slot there.
+            That way we can make u.values a fixed size array with
+            recording the number of entries but of course we then
+            have always N copies for each unwind_to-state.  Or we
+             make sure to only ever append and each unwinding will
+            pop off one entry (but how to deal with predicated
+            replaced with non-predicated here?)  */
+         vno->next = last_inserted_nary;
+         last_inserted_nary = vno;
+         return vno;
+       }
+      else if (vno->predicated_values
+              && ! (*slot)->predicated_values)
+       return *slot;
+      else if (vno->predicated_values
+              && (*slot)->predicated_values)
+       {
+         /* ???  Factor this all into a insert_single_predicated_value
+            routine.  */
+         gcc_assert (!vno->u.values->next && vno->u.values->n == 1);
+         basic_block vno_bb
+           = BASIC_BLOCK_FOR_FN (cfun, vno->u.values->valid_dominated_by_p[0]);
+         vn_pval *nval = vno->u.values;
+         vn_pval **next = &vno->u.values;
+         bool found = false;
+         for (vn_pval *val = (*slot)->u.values; val; val = val->next)
+           {
+             if (expressions_equal_p (val->result, vno->u.values->result))
+               {
+                 found = true;
+                 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, vno_bb, val_bb))
+                       /* Value registered with more generic predicate.  */
+                       return *slot;
+                     else if (dominated_by_p (CDI_DOMINATORS, val_bb, vno_bb))
+                       /* Shouldn't happen, we insert in RPO order.  */
+                       gcc_unreachable ();
+                   }
+                 /* 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] = vno_bb->index;
+                 next = &(*next)->next;
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   fprintf (dump_file, "Appending predicate to value.\n");
+                 continue;
+               }
+             /* Copy other predicated values.  */
+             *next = (vn_pval *) obstack_alloc (&vn_tables_obstack,
+                                                sizeof (vn_pval)
+                                                + (val->n-1) * sizeof (int));
+             memcpy (*next, val, sizeof (vn_pval) + (val->n-1) * sizeof (int));
+             (*next)->next = NULL;
+             next = &(*next)->next;
+           }
+         if (!found)
+           *next = nval;
+
+         *slot = vno;
+         vno->next = last_inserted_nary;
+         last_inserted_nary = vno;
+         return vno;
+       }
+
+      /* While we do not want to insert things twice it's awkward to
+        avoid it in the case where visit_nary_op pattern-matches stuff
+        and ends up simplifying the replacement to itself.  We then
+        get two inserts, one from visit_nary_op and one from
+        vn_nary_build_or_lookup.
+        So allow inserts with the same value number.  */
+      if ((*slot)->u.result == vno->u.result)
+       return *slot;
+    }
+
+  /* ???  There's also optimistic vs. previous commited state merging
+     that is problematic for the case of unwinding.  */
 
+  /* ???  We should return NULL if we do not use 'vno' and have the
+     caller release it.  */
   gcc_assert (!*slot);
 
   *slot = vno;
+  vno->next = last_inserted_nary;
+  last_inserted_nary = vno;
   return vno;
 }
 
@@ -2980,22 +3619,70 @@ vn_nary_op_insert_pieces (unsigned int length, enum tree_code code,
 {
   vn_nary_op_t vno1 = alloc_vn_nary_op (length, result, value_id);
   init_vn_nary_op_from_pieces (vno1, length, code, type, ops);
-  return vn_nary_op_insert_into (vno1, current_info->nary, true);
+  return vn_nary_op_insert_into (vno1, valid_info->nary, true);
 }
 
-/* Insert OP into the current hash table with a value number of
-   RESULT.  Return the vn_nary_op_t structure we created and put in
-   the hashtable.  */
-
-vn_nary_op_t
-vn_nary_op_insert (tree op, tree result)
+static vn_nary_op_t
+vn_nary_op_insert_pieces_predicated (unsigned int length, enum tree_code code,
+                                    tree type, tree *ops,
+                                    tree result, unsigned int value_id,
+                                    edge pred_e)
 {
-  unsigned length = TREE_CODE_LENGTH (TREE_CODE (op));
-  vn_nary_op_t vno1;
+  /* ???  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)
+       return NULL;
+    }
+  if (dump_file && (dump_flags & TDF_DETAILS)
+      /* ???  Fix dumping, but currently we only get comparisons.  */
+      && TREE_CODE_CLASS (code) == tcc_comparison)
+    {
+      fprintf (dump_file, "Recording on edge %d->%d ", pred_e->src->index,
+              pred_e->dest->index);
+      print_generic_expr (dump_file, ops[0], TDF_SLIM);
+      fprintf (dump_file, " %s ", get_tree_code_name (code));
+      print_generic_expr (dump_file, ops[1], TDF_SLIM);
+      fprintf (dump_file, " == %s\n",
+              integer_zerop (result) ? "false" : "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;
+  vno1->u.values = (vn_pval *) obstack_alloc (&vn_tables_obstack,
+                                             sizeof (vn_pval));
+  vno1->u.values->next = NULL;
+  vno1->u.values->result = result;
+  vno1->u.values->n = 1;
+  vno1->u.values->valid_dominated_by_p[0] = pred_e->dest->index;
+  return vn_nary_op_insert_into (vno1, valid_info->nary, true);
+}
 
-  vno1 = alloc_vn_nary_op (length, result, VN_INFO (result)->value_id);
-  init_vn_nary_op_from_op (vno1, op);
-  return vn_nary_op_insert_into (vno1, current_info->nary, true);
+static bool
+dominated_by_p_w_unex (basic_block bb1, basic_block bb2);
+
+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)
+      if (dominated_by_p_w_unex (bb,
+                         BASIC_BLOCK_FOR_FN
+                           (cfun, val->valid_dominated_by_p[i])))
+       return val->result;
+  return NULL_TREE;
 }
 
 /* Insert the rhs of STMT into the current hash table with a value number of
@@ -3008,7 +3695,7 @@ vn_nary_op_insert_stmt (gimple *stmt, tree result)
     = alloc_vn_nary_op (vn_nary_length_from_stmt (stmt),
                        result, VN_INFO (result)->value_id);
   init_vn_nary_op_from_stmt (vno1, stmt);
-  return vn_nary_op_insert_into (vno1, current_info->nary, true);
+  return vn_nary_op_insert_into (vno1, valid_info->nary, true);
 }
 
 /* Compute a hashcode for PHI operation VP1 and return it.  */
@@ -3016,8 +3703,8 @@ vn_nary_op_insert_stmt (gimple *stmt, tree result)
 static inline hashval_t
 vn_phi_compute_hash (vn_phi_t vp1)
 {
-  inchash::hash hstate (vp1->phiargs.length () > 2
-                       ? vp1->block->index : vp1->phiargs.length ());
+  inchash::hash hstate (EDGE_COUNT (vp1->block->preds) > 2
+                       ? vp1->block->index : EDGE_COUNT (vp1->block->preds));
   tree phi1op;
   tree type;
   edge e;
@@ -3089,10 +3776,10 @@ vn_phi_eq (const_vn_phi_t const vp1, const_vn_phi_t const vp2)
 
   if (vp1->block != vp2->block)
     {
-      if (vp1->phiargs.length () != vp2->phiargs.length ())
+      if (EDGE_COUNT (vp1->block->preds) != EDGE_COUNT (vp2->block->preds))
        return false;
 
-      switch (vp1->phiargs.length ())
+      switch (EDGE_COUNT (vp1->block->preds))
        {
        case 1:
          /* Single-arg PHIs are just copies.  */
@@ -3167,10 +3854,9 @@ vn_phi_eq (const_vn_phi_t const vp1, const_vn_phi_t const vp2)
 
   /* Any phi in the same block will have it's arguments in the
      same edge order, because of how we store phi nodes.  */
-  int i;
-  tree phi1op;
-  FOR_EACH_VEC_ELT (vp1->phiargs, i, phi1op)
+  for (unsigned i = 0; i < EDGE_COUNT (vp1->block->preds); ++i)
     {
+      tree phi1op = vp1->phiargs[i];
       tree phi2op = vp2->phiargs[i];
       if (phi1op == VN_TOP || phi2op == VN_TOP)
        continue;
@@ -3181,49 +3867,47 @@ vn_phi_eq (const_vn_phi_t const vp1, const_vn_phi_t const vp2)
   return true;
 }
 
-static vec<tree> shared_lookup_phiargs;
-
 /* Lookup PHI in the current hash table, and return the resulting
    value number if it exists in the hash table.  Return NULL_TREE if
    it does not exist in the hash table. */
 
 static tree
-vn_phi_lookup (gimple *phi)
+vn_phi_lookup (gimple *phi, bool backedges_varying_p)
 {
   vn_phi_s **slot;
-  struct vn_phi_s vp1;
+  struct vn_phi_s *vp1;
   edge e;
   edge_iterator ei;
 
-  shared_lookup_phiargs.truncate (0);
-  shared_lookup_phiargs.safe_grow (gimple_phi_num_args (phi));
+  vp1 = XALLOCAVAR (struct vn_phi_s,
+                   sizeof (struct vn_phi_s)
+                   + (gimple_phi_num_args (phi) - 1) * sizeof (tree));
 
   /* Canonicalize the SSA_NAME's to their value number.  */
   FOR_EACH_EDGE (e, ei, gimple_bb (phi)->preds)
     {
       tree def = PHI_ARG_DEF_FROM_EDGE (phi, e);
-      def = TREE_CODE (def) == SSA_NAME ? SSA_VAL (def) : def;
-      shared_lookup_phiargs[e->dest_idx] = def;
+      if (TREE_CODE (def) == SSA_NAME
+         && (!backedges_varying_p || !(e->flags & EDGE_DFS_BACK)))
+       def = SSA_VAL (def);
+      vp1->phiargs[e->dest_idx] = def;
     }
-  vp1.type = TREE_TYPE (gimple_phi_result (phi));
-  vp1.phiargs = shared_lookup_phiargs;
-  vp1.block = gimple_bb (phi);
+  vp1->type = TREE_TYPE (gimple_phi_result (phi));
+  vp1->block = gimple_bb (phi);
   /* Extract values of the controlling condition.  */
-  vp1.cclhs = NULL_TREE;
-  vp1.ccrhs = NULL_TREE;
-  basic_block idom1 = get_immediate_dominator (CDI_DOMINATORS, vp1.block);
+  vp1->cclhs = NULL_TREE;
+  vp1->ccrhs = NULL_TREE;
+  basic_block idom1 = get_immediate_dominator (CDI_DOMINATORS, vp1->block);
   if (EDGE_COUNT (idom1->succs) == 2)
     if (gcond *last1 = safe_dyn_cast <gcond *> (last_stmt (idom1)))
       {
-       vp1.cclhs = vn_valueize (gimple_cond_lhs (last1));
-       vp1.ccrhs = vn_valueize (gimple_cond_rhs (last1));
+       /* ???  We want to use SSA_VAL here.  But possibly not
+          allow VN_TOP.  */
+       vp1->cclhs = vn_valueize (gimple_cond_lhs (last1));
+       vp1->ccrhs = vn_valueize (gimple_cond_rhs (last1));
       }
-  vp1.hashcode = vn_phi_compute_hash (&vp1);
-  slot = current_info->phis->find_slot_with_hash (&vp1, vp1.hashcode,
-                                                 NO_INSERT);
-  if (!slot && current_info == optimistic_info)
-    slot = valid_info->phis->find_slot_with_hash (&vp1, vp1.hashcode,
-                                                 NO_INSERT);
+  vp1->hashcode = vn_phi_compute_hash (vp1);
+  slot = valid_info->phis->find_slot_with_hash (vp1, vp1->hashcode, NO_INSERT);
   if (!slot)
     return NULL_TREE;
   return (*slot)->result;
@@ -3233,26 +3917,27 @@ vn_phi_lookup (gimple *phi)
    RESULT.  */
 
 static vn_phi_t
-vn_phi_insert (gimple *phi, tree result)
+vn_phi_insert (gimple *phi, tree result, bool backedges_varying_p)
 {
   vn_phi_s **slot;
-  vn_phi_t vp1 = current_info->phis_pool->allocate ();
-  vec<tree> args = vNULL;
+  vn_phi_t vp1 = (vn_phi_t) obstack_alloc (&vn_tables_obstack,
+                                          sizeof (vn_phi_s)
+                                          + ((gimple_phi_num_args (phi) - 1)
+                                             * sizeof (tree)));
   edge e;
   edge_iterator ei;
 
-  args.safe_grow (gimple_phi_num_args (phi));
-
   /* Canonicalize the SSA_NAME's to their value number.  */
   FOR_EACH_EDGE (e, ei, gimple_bb (phi)->preds)
     {
       tree def = PHI_ARG_DEF_FROM_EDGE (phi, e);
-      def = TREE_CODE (def) == SSA_NAME ? SSA_VAL (def) : def;
-      args[e->dest_idx] = def;
+      if (TREE_CODE (def) == SSA_NAME
+         && (!backedges_varying_p || !(e->flags & EDGE_DFS_BACK)))
+       def = SSA_VAL (def);
+      vp1->phiargs[e->dest_idx] = def;
     }
   vp1->value_id = VN_INFO (result)->value_id;
   vp1->type = TREE_TYPE (gimple_phi_result (phi));
-  vp1->phiargs = args;
   vp1->block = gimple_bb (phi);
   /* Extract values of the controlling condition.  */
   vp1->cclhs = NULL_TREE;
@@ -3261,38 +3946,24 @@ vn_phi_insert (gimple *phi, tree result)
   if (EDGE_COUNT (idom1->succs) == 2)
     if (gcond *last1 = safe_dyn_cast <gcond *> (last_stmt (idom1)))
       {
+       /* ???  We want to use SSA_VAL here.  But possibly not
+          allow VN_TOP.  */
        vp1->cclhs = vn_valueize (gimple_cond_lhs (last1));
        vp1->ccrhs = vn_valueize (gimple_cond_rhs (last1));
       }
   vp1->result = result;
   vp1->hashcode = vn_phi_compute_hash (vp1);
 
-  slot = current_info->phis->find_slot_with_hash (vp1, vp1->hashcode, INSERT);
+  slot = valid_info->phis->find_slot_with_hash (vp1, vp1->hashcode, INSERT);
+  gcc_assert (!*slot);
 
-  /* Because we iterate over phi operations more than once, it's
-     possible the slot might already exist here, hence no assert.*/
   *slot = vp1;
+  vp1->next = last_inserted_phi;
+  last_inserted_phi = vp1;
   return vp1;
 }
 
 
-/* Print set of components in strongly connected component SCC to OUT. */
-
-static void
-print_scc (FILE *out, vec<tree> scc)
-{
-  tree var;
-  unsigned int i;
-
-  fprintf (out, "SCC consists of %u:", scc.length ());
-  FOR_EACH_VEC_ELT (scc, i, var)
-    {
-      fprintf (out, " ");
-      print_generic_expr (out, var);
-    }
-  fprintf (out, "\n");
-}
-
 /* Return true if BB1 is dominated by BB2 taking into account edges
    that are not executable.  */
 
@@ -3380,7 +4051,8 @@ dominated_by_p_w_unex (basic_block bb1, basic_block bb2)
 static inline bool
 set_ssa_val_to (tree from, tree to)
 {
-  tree currval = SSA_VAL (from);
+  vn_ssa_aux_t from_info = VN_INFO (from);
+  tree currval = from_info->valnum; // SSA_VAL (from)
   poly_int64 toff, coff;
 
   /* The only thing we allow as value numbers are ssa_names
@@ -3392,16 +4064,23 @@ set_ssa_val_to (tree from, tree to)
      get VN_TOP on valueization.  */
   if (to == VN_TOP)
     {
+      /* ???  When iterating and visiting PHI <undef, backedge-value>
+         for the first time we rightfully get VN_TOP and we need to
+        preserve that to optimize for example gcc.dg/tree-ssa/ssa-sccvn-2.c.
+        With SCCVN we were simply lucky we iterated the other PHI
+        cycles first and thus visited the backedge-value DEF.  */
+      if (currval == VN_TOP)
+       goto set_and_exit;
       if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, "Forcing value number to varying on "
                 "receiving VN_TOP\n");
       to = from;
     }
 
-  gcc_assert (to != NULL_TREE
-             && ((TREE_CODE (to) == SSA_NAME
-                  && (to == from || SSA_VAL (to) == to))
-                 || is_gimple_min_invariant (to)));
+  gcc_checking_assert (to != NULL_TREE
+                      && ((TREE_CODE (to) == SSA_NAME
+                           && (to == from || SSA_VAL (to) == to))
+                          || is_gimple_min_invariant (to)));
 
   if (from != to)
     {
@@ -3417,9 +4096,13 @@ set_ssa_val_to (tree from, tree to)
            }
          return false;
        }
-      else if (currval != VN_TOP
-              && ! is_gimple_min_invariant (currval)
-              && is_gimple_min_invariant (to))
+      bool curr_invariant = is_gimple_min_invariant (currval);
+      bool curr_undefined = (TREE_CODE (currval) == SSA_NAME
+                            && ssa_undefined_value_p (currval, false));
+      if (currval != VN_TOP
+         && !curr_invariant
+         && !curr_undefined
+         && is_gimple_min_invariant (to))
        {
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
@@ -3434,11 +4117,30 @@ set_ssa_val_to (tree from, tree to)
            }
          to = from;
        }
+      else if (currval != VN_TOP
+              && !curr_undefined
+              && TREE_CODE (to) == SSA_NAME
+              && ssa_undefined_value_p (to, false))
+       {
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Forcing VARYING instead of changing "
+                      "value number of ");
+             print_generic_expr (dump_file, from);
+             fprintf (dump_file, " from ");
+             print_generic_expr (dump_file, currval);
+             fprintf (dump_file, " (non-undefined) to ");
+             print_generic_expr (dump_file, to);
+             fprintf (dump_file, " (undefined)\n");
+           }
+         to = from;
+       }
       else if (TREE_CODE (to) == SSA_NAME
               && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (to))
        to = from;
     }
 
+set_and_exit:
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       fprintf (dump_file, "Setting value number of ");
@@ -3467,103 +4169,13 @@ set_ssa_val_to (tree from, tree to)
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, " (changed)\n");
-
-      /* If we equate two SSA names we have to make the side-band info
-         of the leader conservative (and remember whatever original value
-        was present).  */
-      if (TREE_CODE (to) == SSA_NAME)
-       {
-         if (INTEGRAL_TYPE_P (TREE_TYPE (to))
-             && SSA_NAME_RANGE_INFO (to))
-           {
-             if (SSA_NAME_IS_DEFAULT_DEF (to)
-                 || dominated_by_p_w_unex
-                       (gimple_bb (SSA_NAME_DEF_STMT (from)),
-                        gimple_bb (SSA_NAME_DEF_STMT (to))))
-               /* Keep the info from the dominator.  */
-               ;
-             else
-               {
-                 /* Save old info.  */
-                 if (! VN_INFO (to)->info.range_info)
-                   {
-                     VN_INFO (to)->info.range_info = SSA_NAME_RANGE_INFO (to);
-                     VN_INFO (to)->range_info_anti_range_p
-                       = SSA_NAME_ANTI_RANGE_P (to);
-                   }
-                 /* Rather than allocating memory and unioning the info
-                    just clear it.  */
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   {
-                     fprintf (dump_file, "clearing range info of ");
-                     print_generic_expr (dump_file, to);
-                     fprintf (dump_file, "\n");
-                   }
-                 SSA_NAME_RANGE_INFO (to) = NULL;
-               }
-           }
-         else if (POINTER_TYPE_P (TREE_TYPE (to))
-                  && SSA_NAME_PTR_INFO (to))
-           {
-             if (SSA_NAME_IS_DEFAULT_DEF (to)
-                 || dominated_by_p_w_unex
-                       (gimple_bb (SSA_NAME_DEF_STMT (from)),
-                        gimple_bb (SSA_NAME_DEF_STMT (to))))
-               /* Keep the info from the dominator.  */
-               ;
-             else if (! SSA_NAME_PTR_INFO (from)
-                      /* Handle the case of trivially equivalent info.  */
-                      || memcmp (SSA_NAME_PTR_INFO (to),
-                                 SSA_NAME_PTR_INFO (from),
-                                 sizeof (ptr_info_def)) != 0)
-               {
-                 /* Save old info.  */
-                 if (! VN_INFO (to)->info.ptr_info)
-                   VN_INFO (to)->info.ptr_info = SSA_NAME_PTR_INFO (to);
-                 /* Rather than allocating memory and unioning the info
-                    just clear it.  */
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   {
-                     fprintf (dump_file, "clearing points-to info of ");
-                     print_generic_expr (dump_file, to);
-                     fprintf (dump_file, "\n");
-                   }
-                 SSA_NAME_PTR_INFO (to) = NULL;
-               }
-           }
-       }
-
-      VN_INFO (from)->valnum = to;
-      return true;
-    }
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "\n");
-  return false;
-}
-
-/* Mark as processed all the definitions in the defining stmt of USE, or
-   the USE itself.  */
-
-static void
-mark_use_processed (tree use)
-{
-  ssa_op_iter iter;
-  def_operand_p defp;
-  gimple *stmt = SSA_NAME_DEF_STMT (use);
-
-  if (SSA_NAME_IS_DEFAULT_DEF (use) || gimple_code (stmt) == GIMPLE_PHI)
-    {
-      VN_INFO (use)->use_processed = true;
-      return;
-    }
-
-  FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_ALL_DEFS)
-    {
-      tree def = DEF_FROM_PTR (defp);
-
-      VN_INFO (def)->use_processed = true;
-    }
-}
+      from_info->valnum = to;
+      return true;
+    }
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "\n");
+  return false;
+}
 
 /* Set all definitions in STMT to value number to themselves.
    Return true if a value number changed. */
@@ -3602,7 +4214,7 @@ static tree
 valueized_wider_op (tree wide_type, tree op)
 {
   if (TREE_CODE (op) == SSA_NAME)
-    op = SSA_VAL (op);
+    op = vn_valueize (op);
 
   /* Either we have the op widened available.  */
   tree ops[3] = {};
@@ -3623,7 +4235,7 @@ valueized_wider_op (tree wide_type, tree op)
          if (useless_type_conversion_p (wide_type, TREE_TYPE (tem)))
            {
              if (TREE_CODE (tem) == SSA_NAME)
-               tem = SSA_VAL (tem);
+               tem = vn_valueize (tem);
              return tem;
            }
        }
@@ -3642,7 +4254,10 @@ valueized_wider_op (tree wide_type, tree op)
 static bool
 visit_nary_op (tree lhs, gassign *stmt)
 {
-  tree result = vn_nary_op_lookup_stmt (stmt, NULL);
+  vn_nary_op_t vnresult;
+  tree result = vn_nary_op_lookup_stmt (stmt, &vnresult);
+  if (! result && vnresult)
+    result = vn_nary_op_get_predicated_value (vnresult, gimple_bb (stmt));
   if (result)
     return set_ssa_val_to (lhs, result);
 
@@ -3659,8 +4274,12 @@ visit_nary_op (tree lhs, gassign *stmt)
         operation.  */
       if (INTEGRAL_TYPE_P (type)
          && TREE_CODE (rhs1) == SSA_NAME
-         /* We only handle sign-changes or zero-extension -> & mask.  */
-         && ((TYPE_UNSIGNED (TREE_TYPE (rhs1))
+         /* We only handle sign-changes, zero-extension -> & mask or
+            sign-extension if we know the inner operation doesn't
+            overflow.  */
+         && (((TYPE_UNSIGNED (TREE_TYPE (rhs1))
+               || (INTEGRAL_TYPE_P (TREE_TYPE (rhs1))
+                   && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (rhs1))))
               && TYPE_PRECISION (type) > TYPE_PRECISION (TREE_TYPE (rhs1)))
              || TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (rhs1))))
        {
@@ -3682,11 +4301,21 @@ visit_nary_op (tree lhs, gassign *stmt)
                  ops[0] = vn_nary_op_lookup_pieces
                      (2, gimple_assign_rhs_code (def), type, ops, NULL);
                  /* We have wider operation available.  */
-                 if (ops[0])
+                 if (ops[0]
+                     /* If the leader is a wrapping operation we can
+                        insert it for code hoisting w/o introducing
+                        undefined overflow.  If it is not it has to
+                        be available.  See PR86554.  */
+                     && (TYPE_OVERFLOW_WRAPS (TREE_TYPE (ops[0]))
+                         || (rpo_avail && vn_context_bb
+                             && rpo_avail->eliminate_avail (vn_context_bb,
+                                                            ops[0]))))
                    {
                      unsigned lhs_prec = TYPE_PRECISION (type);
                      unsigned rhs_prec = TYPE_PRECISION (TREE_TYPE (rhs1));
-                     if (lhs_prec == rhs_prec)
+                     if (lhs_prec == rhs_prec
+                         || (INTEGRAL_TYPE_P (TREE_TYPE (rhs1))
+                             && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (rhs1))))
                        {
                          gimple_match_op match_op (gimple_match_cond::UNCOND,
                                                    NOP_EXPR, type, ops[0]);
@@ -3781,7 +4410,7 @@ visit_reference_op_call (tree lhs, gcall *stmt)
        }
       if (lhs)
        changed |= set_ssa_val_to (lhs, lhs);
-      vr2 = current_info->references_pool->allocate ();
+      vr2 = XOBNEW (&vn_tables_obstack, vn_reference_s);
       vr2->vuse = vr1.vuse;
       /* As we are not walking the virtual operand chain we know the
         shared_lookup_references are still original so we can re-use
@@ -3792,10 +4421,13 @@ visit_reference_op_call (tree lhs, gcall *stmt)
       vr2->hashcode = vr1.hashcode;
       vr2->result = lhs;
       vr2->result_vdef = vdef_val;
-      slot = current_info->references->find_slot_with_hash (vr2, vr2->hashcode,
-                                                           INSERT);
+      vr2->value_id = 0;
+      slot = valid_info->references->find_slot_with_hash (vr2, vr2->hashcode,
+                                                         INSERT);
       gcc_assert (!*slot);
       *slot = vr2;
+      vr2->next = last_inserted_ref;
+      last_inserted_ref = vr2;
     }
 
   return changed;
@@ -3812,10 +4444,8 @@ visit_reference_op_load (tree lhs, tree op, gimple *stmt)
   tree result;
 
   last_vuse = gimple_vuse (stmt);
-  last_vuse_ptr = &last_vuse;
   result = vn_reference_lookup (op, gimple_vuse (stmt),
-                               default_vn_walk_kind, NULL, true);
-  last_vuse_ptr = NULL;
+                               default_vn_walk_kind, NULL, true, &last_vuse);
 
   /* We handle type-punning through unions by value-numbering based
      on offset and size of the access.  Be prepared to handle a
@@ -3830,6 +4460,10 @@ visit_reference_op_load (tree lhs, tree op, gimple *stmt)
       gimple_match_op res_op (gimple_match_cond::UNCOND,
                              VIEW_CONVERT_EXPR, TREE_TYPE (op), result);
       result = vn_nary_build_or_lookup (&res_op);
+      /* When building the conversion fails avoid inserting the reference
+         again.  */
+      if (!result)
+       return set_ssa_val_to (lhs, lhs);
     }
 
   if (result)
@@ -3881,8 +4515,8 @@ visit_reference_op_store (tree lhs, tree op, gimple *stmt)
       && vnresult->result)
     {
       tree result = vnresult->result;
-      if (TREE_CODE (result) == SSA_NAME)
-       result = SSA_VAL (result);
+      gcc_checking_assert (TREE_CODE (result) != SSA_NAME
+                          || result == SSA_VAL (result));
       resultsame = expressions_equal_p (result, op);
       if (resultsame)
        {
@@ -3905,7 +4539,7 @@ visit_reference_op_store (tree lhs, tree op, gimple *stmt)
          vn_reference_lookup (assign, vuse, VN_NOWALK, &vnresult, false);
          if (vnresult)
            {
-             VN_INFO (vdef)->use_processed = true;
+             VN_INFO (vdef)->visited = true;
              return set_ssa_val_to (vdef, vnresult->result_vdef);
            }
        }
@@ -3953,22 +4587,34 @@ visit_reference_op_store (tree lhs, tree op, gimple *stmt)
 }
 
 /* Visit and value number PHI, return true if the value number
-   changed.  */
+   changed.  When BACKEDGES_VARYING_P is true then assume all
+   backedge values are varying.  When INSERTED is not NULL then
+   this is just a ahead query for a possible iteration, set INSERTED
+   to true if we'd insert into the hashtable.  */
 
 static bool
-visit_phi (gimple *phi)
+visit_phi (gimple *phi, bool *inserted, bool backedges_varying_p)
 {
   tree result, sameval = VN_TOP, seen_undef = NULL_TREE;
+  tree backedge_val = NULL_TREE;
+  bool seen_non_backedge = false;
+  tree sameval_base = NULL_TREE;
+  poly_int64 soff, doff;
   unsigned n_executable = 0;
-  bool allsame = true;
   edge_iterator ei;
   edge e;
 
-  /* TODO: We could check for this in init_sccvn, and replace this
+  /* TODO: We could check for this in initialization, and replace this
      with a gcc_assert.  */
   if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (PHI_RESULT (phi)))
     return set_ssa_val_to (PHI_RESULT (phi), PHI_RESULT (phi));
 
+  /* We track whether a PHI was CSEd to to avoid excessive iterations
+     that would be necessary only because the PHI changed arguments
+     but not value.  */
+  if (!inserted)
+    gimple_set_plf (phi, GF_PLF_1, false);
+
   /* See if all non-TOP arguments have the same value.  TOP is
      equivalent to everything, so we can ignore it.  */
   FOR_EACH_EDGE (e, ei, gimple_bb (phi)->preds)
@@ -3978,26 +4624,74 @@ visit_phi (gimple *phi)
 
        ++n_executable;
        if (TREE_CODE (def) == SSA_NAME)
-         def = SSA_VAL (def);
+         {
+           if (!backedges_varying_p || !(e->flags & EDGE_DFS_BACK))
+             def = SSA_VAL (def);
+           if (e->flags & EDGE_DFS_BACK)
+             backedge_val = def;
+         }
+       if (!(e->flags & EDGE_DFS_BACK))
+         seen_non_backedge = true;
        if (def == VN_TOP)
          ;
        /* Ignore undefined defs for sameval but record one.  */
        else if (TREE_CODE (def) == SSA_NAME
+                && ! virtual_operand_p (def)
                 && ssa_undefined_value_p (def, false))
          seen_undef = def;
        else if (sameval == VN_TOP)
          sameval = def;
        else if (!expressions_equal_p (def, sameval))
          {
-           allsame = false;
+           /* We know we're arriving only with invariant addresses here,
+              try harder comparing them.  We can do some caching here
+              which we cannot do in expressions_equal_p.  */
+           if (TREE_CODE (def) == ADDR_EXPR
+               && TREE_CODE (sameval) == ADDR_EXPR
+               && sameval_base != (void *)-1)
+             {
+               if (!sameval_base)
+                 sameval_base = get_addr_base_and_unit_offset
+                                  (TREE_OPERAND (sameval, 0), &soff);
+               if (!sameval_base)
+                 sameval_base = (tree)(void *)-1;
+               else if ((get_addr_base_and_unit_offset
+                           (TREE_OPERAND (def, 0), &doff) == sameval_base)
+                        && known_eq (soff, doff))
+                 continue;
+             }
+           sameval = NULL_TREE;
            break;
          }
       }
 
-
+  /* If the value we want to use is flowing over the backedge and we
+     should take it as VARYING but it has a non-VARYING value drop to
+     VARYING.
+     If we value-number a virtual operand never value-number to the
+     value from the backedge as that confuses the alias-walking code.
+     See gcc.dg/torture/pr87176.c.  If the value is the same on a
+     non-backedge everything is OK though.  */
+  bool visited_p;
+  if ((backedge_val
+       && !seen_non_backedge
+       && TREE_CODE (backedge_val) == SSA_NAME
+       && sameval == backedge_val
+       && (SSA_NAME_IS_VIRTUAL_OPERAND (backedge_val)
+          || SSA_VAL (backedge_val) != backedge_val))
+      /* Do not value-number a virtual operand to sth not visited though
+        given that allows us to escape a region in alias walking.  */
+      || (sameval
+         && TREE_CODE (sameval) == SSA_NAME
+         && !SSA_NAME_IS_DEFAULT_DEF (sameval)
+         && SSA_NAME_IS_VIRTUAL_OPERAND (sameval)
+         && (SSA_VAL (sameval, &visited_p), !visited_p)))
+    /* Note this just drops to VARYING without inserting the PHI into
+       the hashes.  */
+    result = PHI_RESULT (phi);
   /* If none of the edges was executable keep the value-number at VN_TOP,
      if only a single edge is exectuable use its value.  */
-  if (n_executable <= 1)
+  else if (n_executable <= 1)
     result = seen_undef ? seen_undef : sameval;
   /* If we saw only undefined values and VN_TOP use one of the
      undefined values.  */
@@ -4005,12 +4699,26 @@ visit_phi (gimple *phi)
     result = seen_undef ? seen_undef : sameval;
   /* First see if it is equivalent to a phi node in this block.  We prefer
      this as it allows IV elimination - see PRs 66502 and 67167.  */
-  else if ((result = vn_phi_lookup (phi)))
-    ;
+  else if ((result = vn_phi_lookup (phi, backedges_varying_p)))
+    {
+      if (!inserted
+         && TREE_CODE (result) == SSA_NAME
+         && gimple_code (SSA_NAME_DEF_STMT (result)) == GIMPLE_PHI)
+       {
+         gimple_set_plf (SSA_NAME_DEF_STMT (result), GF_PLF_1, true);
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Marking CSEd to PHI node ");
+             print_gimple_expr (dump_file, SSA_NAME_DEF_STMT (result),
+                                0, TDF_SLIM);
+             fprintf (dump_file, "\n");
+           }
+       }
+    }
   /* If all values are the same use that, unless we've seen undefined
      values as well and the value isn't constant.
      CCP/copyprop have the same restriction to not remove uninit warnings.  */
-  else if (allsame
+  else if (sameval
           && (! seen_undef || is_gimple_min_invariant (sameval)))
     result = sameval;
   else
@@ -4019,7 +4727,9 @@ visit_phi (gimple *phi)
       /* Only insert PHIs that are varying, for constant value numbers
          we mess up equivalences otherwise as we are only comparing
         the immediate controlling predicates.  */
-      vn_phi_insert (phi, result);
+      vn_phi_insert (phi, result, backedges_varying_p);
+      if (inserted)
+       *inserted = true;
     }
 
   return set_ssa_val_to (PHI_RESULT (phi), result);
@@ -4050,30 +4760,22 @@ try_to_simplify (gassign *stmt)
   return NULL_TREE;
 }
 
-/* Visit and value number USE, return true if the value number
-   changed. */
+/* Visit and value number STMT, return true if the value number
+   changed.  */
 
 static bool
-visit_use (tree use)
+visit_stmt (gimple *stmt, bool backedges_varying_p = false)
 {
   bool changed = false;
-  gimple *stmt = SSA_NAME_DEF_STMT (use);
-
-  mark_use_processed (use);
-
-  gcc_assert (!SSA_NAME_IN_FREE_LIST (use)
-             && !SSA_NAME_IS_DEFAULT_DEF (use));
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
-      fprintf (dump_file, "Value numbering ");
-      print_generic_expr (dump_file, use);
-      fprintf (dump_file, " stmt = ");
+      fprintf (dump_file, "Value numbering stmt = ");
       print_gimple_stmt (dump_file, stmt, 0);
     }
 
   if (gimple_code (stmt) == GIMPLE_PHI)
-    changed = visit_phi (stmt);
+    changed = visit_phi (stmt, NULL, backedges_varying_p);
   else if (gimple_has_volatile_ops (stmt))
     changed = defs_to_varying (stmt);
   else if (gassign *ass = dyn_cast <gassign *> (stmt))
@@ -4260,842 +4962,1107 @@ visit_use (tree use)
   return changed;
 }
 
-/* Compare two operands by reverse postorder index */
 
-static int
-compare_ops (const void *pa, const void *pb)
-{
-  const tree opa = *((const tree *)pa);
-  const tree opb = *((const tree *)pb);
-  gimple *opstmta = SSA_NAME_DEF_STMT (opa);
-  gimple *opstmtb = SSA_NAME_DEF_STMT (opb);
-  basic_block bba;
-  basic_block bbb;
-
-  if (gimple_nop_p (opstmta) && gimple_nop_p (opstmtb))
-    return SSA_NAME_VERSION (opa) - SSA_NAME_VERSION (opb);
-  else if (gimple_nop_p (opstmta))
-    return -1;
-  else if (gimple_nop_p (opstmtb))
-    return 1;
+/* Allocate a value number table.  */
 
-  bba = gimple_bb (opstmta);
-  bbb = gimple_bb (opstmtb);
+static void
+allocate_vn_table (vn_tables_t table, unsigned size)
+{
+  table->phis = new vn_phi_table_type (size);
+  table->nary = new vn_nary_op_table_type (size);
+  table->references = new vn_reference_table_type (size);
+}
 
-  if (!bba && !bbb)
-    return SSA_NAME_VERSION (opa) - SSA_NAME_VERSION (opb);
-  else if (!bba)
-    return -1;
-  else if (!bbb)
-    return 1;
+/* Free a value number table.  */
 
-  if (bba == bbb)
-    {
-      if (gimple_code (opstmta) == GIMPLE_PHI
-         && gimple_code (opstmtb) == GIMPLE_PHI)
-       return SSA_NAME_VERSION (opa) - SSA_NAME_VERSION (opb);
-      else if (gimple_code (opstmta) == GIMPLE_PHI)
-       return -1;
-      else if (gimple_code (opstmtb) == GIMPLE_PHI)
-       return 1;
-      else if (gimple_uid (opstmta) != gimple_uid (opstmtb))
-        return gimple_uid (opstmta) - gimple_uid (opstmtb);
-      else
-       return SSA_NAME_VERSION (opa) - SSA_NAME_VERSION (opb);
-    }
-  return rpo_numbers[bba->index] - rpo_numbers[bbb->index];
+static void
+free_vn_table (vn_tables_t table)
+{
+  /* Walk over elements and release vectors.  */
+  vn_reference_iterator_type hir;
+  vn_reference_t vr;
+  FOR_EACH_HASH_TABLE_ELEMENT (*table->references, vr, vn_reference_t, hir)
+    vr->operands.release ();
+  delete table->phis;
+  table->phis = NULL;
+  delete table->nary;
+  table->nary = NULL;
+  delete table->references;
+  table->references = NULL;
 }
 
-/* Sort an array containing members of a strongly connected component
-   SCC so that the members are ordered by RPO number.
-   This means that when the sort is complete, iterating through the
-   array will give you the members in RPO order.  */
+/* Set *ID according to RESULT.  */
 
 static void
-sort_scc (vec<tree> scc)
+set_value_id_for_result (tree result, unsigned int *id)
 {
-  scc.qsort (compare_ops);
+  if (result && TREE_CODE (result) == SSA_NAME)
+    *id = VN_INFO (result)->value_id;
+  else if (result && is_gimple_min_invariant (result))
+    *id = get_or_alloc_constant_value_id (result);
+  else
+    *id = get_next_value_id ();
 }
 
-/* Insert the no longer used nary ONARY to the hash INFO.  */
+/* Set the value ids in the valid hash tables.  */
 
 static void
-copy_nary (vn_nary_op_t onary, vn_tables_t info)
+set_hashtable_value_ids (void)
 {
-  size_t size = sizeof_vn_nary_op (onary->length);
-  vn_nary_op_t nary = alloc_vn_nary_op_noinit (onary->length,
-                                              &info->nary_obstack);
-  memcpy (nary, onary, size);
-  vn_nary_op_insert_into (nary, info->nary, false);
+  vn_nary_op_iterator_type hin;
+  vn_phi_iterator_type hip;
+  vn_reference_iterator_type hir;
+  vn_nary_op_t vno;
+  vn_reference_t vr;
+  vn_phi_t vp;
+
+  /* Now set the value ids of the things we had put in the hash
+     table.  */
+
+  FOR_EACH_HASH_TABLE_ELEMENT (*valid_info->nary, vno, vn_nary_op_t, hin)
+    if (! vno->predicated_values)
+      set_value_id_for_result (vno->u.result, &vno->value_id);
+
+  FOR_EACH_HASH_TABLE_ELEMENT (*valid_info->phis, vp, vn_phi_t, hip)
+    set_value_id_for_result (vp->result, &vp->value_id);
+
+  FOR_EACH_HASH_TABLE_ELEMENT (*valid_info->references, vr, vn_reference_t,
+                              hir)
+    set_value_id_for_result (vr->result, &vr->value_id);
 }
 
-/* Insert the no longer used phi OPHI to the hash INFO.  */
+/* Return the maximum value id we have ever seen.  */
 
-static void
-copy_phi (vn_phi_t ophi, vn_tables_t info)
+unsigned int
+get_max_value_id (void)
 {
-  vn_phi_t phi = info->phis_pool->allocate ();
-  vn_phi_s **slot;
-  memcpy (phi, ophi, sizeof (*phi));
-  ophi->phiargs.create (0);
-  slot = info->phis->find_slot_with_hash (phi, phi->hashcode, INSERT);
-  gcc_assert (!*slot);
-  *slot = phi;
+  return next_value_id;
 }
 
-/* Insert the no longer used reference OREF to the hash INFO.  */
+/* Return the next unique value id.  */
 
-static void
-copy_reference (vn_reference_t oref, vn_tables_t info)
+unsigned int
+get_next_value_id (void)
 {
-  vn_reference_t ref;
-  vn_reference_s **slot;
-  ref = info->references_pool->allocate ();
-  memcpy (ref, oref, sizeof (*ref));
-  oref->operands.create (0);
-  slot = info->references->find_slot_with_hash (ref, ref->hashcode, INSERT);
-  if (*slot)
-    free_reference (*slot);
-  *slot = ref;
+  return next_value_id++;
 }
 
-/* Process a strongly connected component in the SSA graph.  */
 
-static void
-process_scc (vec<tree> scc)
+/* Compare two expressions E1 and E2 and return true if they are equal.  */
+
+bool
+expressions_equal_p (tree e1, tree e2)
 {
-  tree var;
-  unsigned int i;
-  unsigned int iterations = 0;
-  bool changed = true;
-  vn_nary_op_iterator_type hin;
-  vn_phi_iterator_type hip;
-  vn_reference_iterator_type hir;
-  vn_nary_op_t nary;
-  vn_phi_t phi;
-  vn_reference_t ref;
+  /* The obvious case.  */
+  if (e1 == e2)
+    return true;
 
-  /* If the SCC has a single member, just visit it.  */
-  if (scc.length () == 1)
-    {
-      tree use = scc[0];
-      if (VN_INFO (use)->use_processed)
-       return;
-      /* We need to make sure it doesn't form a cycle itself, which can
-        happen for self-referential PHI nodes.  In that case we would
-        end up inserting an expression with VN_TOP operands into the
-        valid table which makes us derive bogus equivalences later.
-        The cheapest way to check this is to assume it for all PHI nodes.  */
-      if (gimple_code (SSA_NAME_DEF_STMT (use)) == GIMPLE_PHI)
-       /* Fallthru to iteration.  */ ;
-      else
-       {
-         visit_use (use);
-         return;
-       }
-    }
+  /* If either one is VN_TOP consider them equal.  */
+  if (e1 == VN_TOP || e2 == VN_TOP)
+    return true;
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    print_scc (dump_file, scc);
+  /* If only one of them is null, they cannot be equal.  */
+  if (!e1 || !e2)
+    return false;
 
-  /* Iterate over the SCC with the optimistic table until it stops
-     changing.  */
-  current_info = optimistic_info;
-  while (changed)
-    {
-      changed = false;
-      iterations++;
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       fprintf (dump_file, "Starting iteration %d\n", iterations);
-      /* As we are value-numbering optimistically we have to
-        clear the expression tables and the simplified expressions
-        in each iteration until we converge.  */
-      optimistic_info->nary->empty ();
-      optimistic_info->phis->empty ();
-      optimistic_info->references->empty ();
-      obstack_free (&optimistic_info->nary_obstack, NULL);
-      gcc_obstack_init (&optimistic_info->nary_obstack);
-      optimistic_info->phis_pool->release ();
-      optimistic_info->references_pool->release ();
-      FOR_EACH_VEC_ELT (scc, i, var)
-       gcc_assert (!VN_INFO (var)->needs_insertion
-                   && VN_INFO (var)->expr == NULL);
-      FOR_EACH_VEC_ELT (scc, i, var)
-       changed |= visit_use (var);
-    }
+  /* Now perform the actual comparison.  */
+  if (TREE_CODE (e1) == TREE_CODE (e2)
+      && operand_equal_p (e1, e2, OEP_PURE_SAME))
+    return true;
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "Processing SCC needed %d iterations\n", iterations);
-  statistics_histogram_event (cfun, "SCC iterations", iterations);
-
-  /* Finally, copy the contents of the no longer used optimistic
-     table to the valid table.  */
-  FOR_EACH_HASH_TABLE_ELEMENT (*optimistic_info->nary, nary, vn_nary_op_t, hin)
-    copy_nary (nary, valid_info);
-  FOR_EACH_HASH_TABLE_ELEMENT (*optimistic_info->phis, phi, vn_phi_t, hip)
-    copy_phi (phi, valid_info);
-  FOR_EACH_HASH_TABLE_ELEMENT (*optimistic_info->references,
-                              ref, vn_reference_t, hir)
-    copy_reference (ref, valid_info);
-
-  current_info = valid_info;
+  return false;
 }
 
 
-/* Pop the components of the found SCC for NAME off the SCC stack
-   and process them.  Returns true if all went well, false if
-   we run into resource limits.  */
+/* Return true if the nary operation NARY may trap.  This is a copy
+   of stmt_could_throw_1_p adjusted to the SCCVN IL.  */
 
-static void
-extract_and_process_scc_for_name (tree name)
+bool
+vn_nary_may_trap (vn_nary_op_t nary)
 {
-  auto_vec<tree> scc;
-  tree x;
-
-  /* Found an SCC, pop the components off the SCC stack and
-     process them.  */
-  do
-    {
-      x = sccstack.pop ();
-
-      VN_INFO (x)->on_sccstack = false;
-      scc.safe_push (x);
-    } while (x != name);
+  tree type;
+  tree rhs2 = NULL_TREE;
+  bool honor_nans = false;
+  bool honor_snans = false;
+  bool fp_operation = false;
+  bool honor_trapv = false;
+  bool handled, ret;
+  unsigned i;
 
-  /* Drop all defs in the SCC to varying in case a SCC turns out to be
-     incredibly large.
-     ???  Just switch to a non-optimistic mode that avoids any iteration.  */
-  if (scc.length () > (unsigned)PARAM_VALUE (PARAM_SCCVN_MAX_SCC_SIZE))
+  if (TREE_CODE_CLASS (nary->opcode) == tcc_comparison
+      || TREE_CODE_CLASS (nary->opcode) == tcc_unary
+      || TREE_CODE_CLASS (nary->opcode) == tcc_binary)
     {
-      if (dump_file)
-       {
-         print_scc (dump_file, scc);
-         fprintf (dump_file, "WARNING: Giving up value-numbering SCC due to "
-                  "size %u exceeding %u\n", scc.length (),
-                  (unsigned)PARAM_VALUE (PARAM_SCCVN_MAX_SCC_SIZE));
-       }
-      tree var;
-      unsigned i;
-      FOR_EACH_VEC_ELT (scc, i, var)
+      type = nary->type;
+      fp_operation = FLOAT_TYPE_P (type);
+      if (fp_operation)
        {
-         gimple *def = SSA_NAME_DEF_STMT (var);
-         mark_use_processed (var);
-         if (SSA_NAME_IS_DEFAULT_DEF (var)
-             || gimple_code (def) == GIMPLE_PHI)
-           set_ssa_val_to (var, var);
-         else
-           defs_to_varying (def);
+         honor_nans = flag_trapping_math && !flag_finite_math_only;
+         honor_snans = flag_signaling_nans != 0;
        }
-      return;
+      else if (INTEGRAL_TYPE_P (type)
+              && TYPE_OVERFLOW_TRAPS (type))
+       honor_trapv = true;
     }
+  if (nary->length >= 2)
+    rhs2 = nary->op[1];
+  ret = operation_could_trap_helper_p (nary->opcode, fp_operation,
+                                      honor_trapv,
+                                      honor_nans, honor_snans, rhs2,
+                                      &handled);
+  if (handled
+      && ret)
+    return true;
 
-  if (scc.length () > 1)
-    sort_scc (scc);
+  for (i = 0; i < nary->length; ++i)
+    if (tree_could_trap_p (nary->op[i]))
+      return true;
 
-  process_scc (scc);
+  return false;
 }
 
-/* Depth first search on NAME to discover and process SCC's in the SSA
-   graph.
-   Execution of this algorithm relies on the fact that the SCC's are
-   popped off the stack in topological order.
-   Returns true if successful, false if we stopped processing SCC's due
-   to resource constraints.  */
+/* Return true if the reference operation REF may trap.  */
 
-static void
-DFS (tree name)
+bool
+vn_reference_may_trap (vn_reference_t ref)
 {
-  auto_vec<ssa_op_iter> itervec;
-  auto_vec<tree> namevec;
-  use_operand_p usep = NULL;
-  gimple *defstmt;
-  tree use;
-  ssa_op_iter iter;
-
-start_over:
-  /* SCC info */
-  VN_INFO (name)->dfsnum = next_dfs_num++;
-  VN_INFO (name)->visited = true;
-  VN_INFO (name)->low = VN_INFO (name)->dfsnum;
-
-  sccstack.safe_push (name);
-  VN_INFO (name)->on_sccstack = true;
-  defstmt = SSA_NAME_DEF_STMT (name);
-
-  /* Recursively DFS on our operands, looking for SCC's.  */
-  if (!gimple_nop_p (defstmt))
+  switch (ref->operands[0].opcode)
     {
-      /* Push a new iterator.  */
-      if (gphi *phi = dyn_cast <gphi *> (defstmt))
-       usep = op_iter_init_phiuse (&iter, phi, SSA_OP_ALL_USES);
-      else
-       usep = op_iter_init_use (&iter, defstmt, SSA_OP_ALL_USES);
+    case MODIFY_EXPR:
+    case CALL_EXPR:
+      /* We do not handle calls.  */
+    case ADDR_EXPR:
+      /* And toplevel address computations never trap.  */
+      return false;
+    default:;
     }
-  else
-    clear_and_done_ssa_iter (&iter);
 
-  while (1)
+  vn_reference_op_t op;
+  unsigned i;
+  FOR_EACH_VEC_ELT (ref->operands, i, op)
     {
-      /* If we are done processing uses of a name, go up the stack
-        of iterators and process SCCs as we found them.  */
-      if (op_iter_done (&iter))
-       {
-         /* See if we found an SCC.  */
-         if (VN_INFO (name)->low == VN_INFO (name)->dfsnum)
-           extract_and_process_scc_for_name (name);
-
-         /* Check if we are done.  */
-         if (namevec.is_empty ())
-           return;
-
-         /* Restore the last use walker and continue walking there.  */
-         use = name;
-         name = namevec.pop ();
-         memcpy (&iter, &itervec.last (),
-                 sizeof (ssa_op_iter));
-         itervec.pop ();
-         goto continue_walking;
-       }
-
-      use = USE_FROM_PTR (usep);
-
-      /* Since we handle phi nodes, we will sometimes get
-        invariants in the use expression.  */
-      if (TREE_CODE (use) == SSA_NAME)
+      switch (op->opcode)
        {
-         if (! (VN_INFO (use)->visited))
-           {
-             /* Recurse by pushing the current use walking state on
-                the stack and starting over.  */
-             itervec.safe_push (iter);
-             namevec.safe_push (name);
-             name = use;
-             goto start_over;
-
-continue_walking:
-             VN_INFO (name)->low = MIN (VN_INFO (name)->low,
-                                        VN_INFO (use)->low);
-           }
-         if (VN_INFO (use)->dfsnum < VN_INFO (name)->dfsnum
-             && VN_INFO (use)->on_sccstack)
-           {
-             VN_INFO (name)->low = MIN (VN_INFO (use)->dfsnum,
-                                        VN_INFO (name)->low);
-           }
+       case WITH_SIZE_EXPR:
+       case TARGET_MEM_REF:
+         /* Always variable.  */
+         return true;
+       case COMPONENT_REF:
+         if (op->op1 && TREE_CODE (op->op1) == SSA_NAME)
+           return true;
+         break;
+       case ARRAY_RANGE_REF:
+       case ARRAY_REF:
+         if (TREE_CODE (op->op0) == SSA_NAME)
+           return true;
+         break;
+       case MEM_REF:
+         /* Nothing interesting in itself, the base is separate.  */
+         break;
+       /* The following are the address bases.  */
+       case SSA_NAME:
+         return true;
+       case ADDR_EXPR:
+         if (op->op0)
+           return tree_could_trap_p (TREE_OPERAND (op->op0, 0));
+         return false;
+       default:;
        }
-
-      usep = op_iter_next_use (&iter);
     }
+  return false;
 }
 
-/* Allocate a value number table.  */
-
-static void
-allocate_vn_table (vn_tables_t table)
+eliminate_dom_walker::eliminate_dom_walker (cdi_direction direction,
+                                           bitmap inserted_exprs_)
+  : dom_walker (direction), do_pre (inserted_exprs_ != NULL),
+    el_todo (0), eliminations (0), insertions (0),
+    inserted_exprs (inserted_exprs_)
 {
-  table->phis = new vn_phi_table_type (23);
-  table->nary = new vn_nary_op_table_type (23);
-  table->references = new vn_reference_table_type (23);
-
-  gcc_obstack_init (&table->nary_obstack);
-  table->phis_pool = new object_allocator<vn_phi_s> ("VN phis");
-  table->references_pool = new object_allocator<vn_reference_s>
-    ("VN references");
+  need_eh_cleanup = BITMAP_ALLOC (NULL);
+  need_ab_cleanup = BITMAP_ALLOC (NULL);
 }
 
-/* Free a value number table.  */
-
-static void
-free_vn_table (vn_tables_t table)
+eliminate_dom_walker::~eliminate_dom_walker ()
 {
-  delete table->phis;
-  table->phis = NULL;
-  delete table->nary;
-  table->nary = NULL;
-  delete table->references;
-  table->references = NULL;
-  obstack_free (&table->nary_obstack, NULL);
-  delete table->phis_pool;
-  delete table->references_pool;
+  BITMAP_FREE (need_eh_cleanup);
+  BITMAP_FREE (need_ab_cleanup);
 }
 
-static void
-init_scc_vn (void)
-{
-  int j;
-  int *rpo_numbers_temp;
+/* Return a leader for OP that is available at the current point of the
+   eliminate domwalk.  */
 
-  calculate_dominance_info (CDI_DOMINATORS);
-  mark_dfs_back_edges ();
+tree
+eliminate_dom_walker::eliminate_avail (basic_block, tree op)
+{
+  tree valnum = VN_INFO (op)->valnum;
+  if (TREE_CODE (valnum) == SSA_NAME)
+    {
+      if (SSA_NAME_IS_DEFAULT_DEF (valnum))
+       return valnum;
+      if (avail.length () > SSA_NAME_VERSION (valnum))
+       return avail[SSA_NAME_VERSION (valnum)];
+    }
+  else if (is_gimple_min_invariant (valnum))
+    return valnum;
+  return NULL_TREE;
+}
 
-  sccstack.create (0);
-  constant_to_value_id = new hash_table<vn_constant_hasher> (23);
+/* At the current point of the eliminate domwalk make OP available.  */
 
-  constant_value_ids = BITMAP_ALLOC (NULL);
+void
+eliminate_dom_walker::eliminate_push_avail (basic_block, tree op)
+{
+  tree valnum = VN_INFO (op)->valnum;
+  if (TREE_CODE (valnum) == SSA_NAME)
+    {
+      if (avail.length () <= SSA_NAME_VERSION (valnum))
+       avail.safe_grow_cleared (SSA_NAME_VERSION (valnum) + 1);
+      tree pushop = op;
+      if (avail[SSA_NAME_VERSION (valnum)])
+       pushop = avail[SSA_NAME_VERSION (valnum)];
+      avail_stack.safe_push (pushop);
+      avail[SSA_NAME_VERSION (valnum)] = op;
+    }
+}
 
-  next_dfs_num = 1;
-  next_value_id = 1;
+/* Insert the expression recorded by SCCVN for VAL at *GSI.  Returns
+   the leader for the expression if insertion was successful.  */
 
-  vn_ssa_aux_table.create (num_ssa_names + 1);
-  /* VEC_alloc doesn't actually grow it to the right size, it just
-     preallocates the space to do so.  */
-  vn_ssa_aux_table.safe_grow_cleared (num_ssa_names + 1);
-  gcc_obstack_init (&vn_ssa_aux_obstack);
+tree
+eliminate_dom_walker::eliminate_insert (basic_block bb,
+                                       gimple_stmt_iterator *gsi, tree val)
+{
+  /* We can insert a sequence with a single assignment only.  */
+  gimple_seq stmts = VN_INFO (val)->expr;
+  if (!gimple_seq_singleton_p (stmts))
+    return NULL_TREE;
+  gassign *stmt = dyn_cast <gassign *> (gimple_seq_first_stmt (stmts));
+  if (!stmt
+      || (!CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
+         && gimple_assign_rhs_code (stmt) != VIEW_CONVERT_EXPR
+         && gimple_assign_rhs_code (stmt) != BIT_FIELD_REF
+         && (gimple_assign_rhs_code (stmt) != BIT_AND_EXPR
+             || TREE_CODE (gimple_assign_rhs2 (stmt)) != INTEGER_CST)))
+    return NULL_TREE;
 
-  shared_lookup_phiargs.create (0);
-  shared_lookup_references.create (0);
-  rpo_numbers = XNEWVEC (int, last_basic_block_for_fn (cfun));
-  rpo_numbers_temp =
-    XNEWVEC (int, n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS);
-  pre_and_rev_post_order_compute (NULL, rpo_numbers_temp, false);
+  tree op = gimple_assign_rhs1 (stmt);
+  if (gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
+      || gimple_assign_rhs_code (stmt) == BIT_FIELD_REF)
+    op = TREE_OPERAND (op, 0);
+  tree leader = TREE_CODE (op) == SSA_NAME ? eliminate_avail (bb, op) : op;
+  if (!leader)
+    return NULL_TREE;
 
-  /* RPO numbers is an array of rpo ordering, rpo[i] = bb means that
-     the i'th block in RPO order is bb.  We want to map bb's to RPO
-     numbers, so we need to rearrange this array.  */
-  for (j = 0; j < n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS; j++)
-    rpo_numbers[rpo_numbers_temp[j]] = j;
+  tree res;
+  stmts = NULL;
+  if (gimple_assign_rhs_code (stmt) == BIT_FIELD_REF)
+    res = gimple_build (&stmts, BIT_FIELD_REF,
+                       TREE_TYPE (val), leader,
+                       TREE_OPERAND (gimple_assign_rhs1 (stmt), 1),
+                       TREE_OPERAND (gimple_assign_rhs1 (stmt), 2));
+  else if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR)
+    res = gimple_build (&stmts, BIT_AND_EXPR,
+                       TREE_TYPE (val), leader, gimple_assign_rhs2 (stmt));
+  else
+    res = gimple_build (&stmts, gimple_assign_rhs_code (stmt),
+                       TREE_TYPE (val), leader);
+  if (TREE_CODE (res) != SSA_NAME
+      || SSA_NAME_IS_DEFAULT_DEF (res)
+      || gimple_bb (SSA_NAME_DEF_STMT (res)))
+    {
+      gimple_seq_discard (stmts);
 
-  XDELETE (rpo_numbers_temp);
+      /* During propagation we have to treat SSA info conservatively
+         and thus we can end up simplifying the inserted expression
+        at elimination time to sth not defined in stmts.  */
+      /* But then this is a redundancy we failed to detect.  Which means
+         res now has two values.  That doesn't play well with how
+        we track availability here, so give up.  */
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         if (TREE_CODE (res) == SSA_NAME)
+           res = eliminate_avail (bb, res);
+         if (res)
+           {
+             fprintf (dump_file, "Failed to insert expression for value ");
+             print_generic_expr (dump_file, val);
+             fprintf (dump_file, " which is really fully redundant to ");
+             print_generic_expr (dump_file, res);
+             fprintf (dump_file, "\n");
+           }
+       }
 
-  VN_TOP = build_decl (UNKNOWN_LOCATION, VAR_DECL,
-                      get_identifier ("VN_TOP"), error_mark_node);
+      return NULL_TREE;
+    }
+  else
+    {
+      gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+      VN_INFO (res)->valnum = val;
+      VN_INFO (res)->visited = true;
+    }
 
-  renumber_gimple_stmt_uids ();
+  insertions++;
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Inserted ");
+      print_gimple_stmt (dump_file, SSA_NAME_DEF_STMT (res), 0);
+    }
 
-  /* Create the valid and optimistic value numbering tables.  */
-  valid_info = XCNEW (struct vn_tables_s);
-  allocate_vn_table (valid_info);
-  optimistic_info = XCNEW (struct vn_tables_s);
-  allocate_vn_table (optimistic_info);
-  current_info = valid_info;
-
-  /* Create the VN_INFO structures, and initialize value numbers to
-     TOP or VARYING for parameters.  */
-  size_t i;
-  tree name;
+  return res;
+}
 
-  FOR_EACH_SSA_NAME (i, name, cfun)
+void
+eliminate_dom_walker::eliminate_stmt (basic_block b, gimple_stmt_iterator *gsi)
+{
+  tree sprime = NULL_TREE;
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_get_lhs (stmt);
+  if (lhs && TREE_CODE (lhs) == SSA_NAME
+      && !gimple_has_volatile_ops (stmt)
+      /* See PR43491.  Do not replace a global register variable when
+        it is a the RHS of an assignment.  Do replace local register
+        variables since gcc does not guarantee a local variable will
+        be allocated in register.
+        ???  The fix isn't effective here.  This should instead
+        be ensured by not value-numbering them the same but treating
+        them like volatiles?  */
+      && !(gimple_assign_single_p (stmt)
+          && (TREE_CODE (gimple_assign_rhs1 (stmt)) == VAR_DECL
+              && DECL_HARD_REGISTER (gimple_assign_rhs1 (stmt))
+              && is_global_var (gimple_assign_rhs1 (stmt)))))
     {
-      VN_INFO_GET (name)->valnum = VN_TOP;
-      VN_INFO (name)->needs_insertion = false;
-      VN_INFO (name)->expr = NULL;
-      VN_INFO (name)->value_id = 0;
+      sprime = eliminate_avail (b, lhs);
+      if (!sprime)
+       {
+         /* If there is no existing usable leader but SCCVN thinks
+            it has an expression it wants to use as replacement,
+            insert that.  */
+         tree val = VN_INFO (lhs)->valnum;
+         if (val != VN_TOP
+             && TREE_CODE (val) == SSA_NAME
+             && VN_INFO (val)->needs_insertion
+             && VN_INFO (val)->expr != NULL
+             && (sprime = eliminate_insert (b, gsi, val)) != NULL_TREE)
+           eliminate_push_avail (b, sprime);
+       }
 
-      if (!SSA_NAME_IS_DEFAULT_DEF (name))
-       continue;
+      /* If this now constitutes a copy duplicate points-to
+        and range info appropriately.  This is especially
+        important for inserted code.  See tree-ssa-copy.c
+        for similar code.  */
+      if (sprime
+         && TREE_CODE (sprime) == SSA_NAME)
+       {
+         basic_block sprime_b = gimple_bb (SSA_NAME_DEF_STMT (sprime));
+         if (POINTER_TYPE_P (TREE_TYPE (lhs))
+             && SSA_NAME_PTR_INFO (lhs)
+             && ! SSA_NAME_PTR_INFO (sprime))
+           {
+             duplicate_ssa_name_ptr_info (sprime,
+                                          SSA_NAME_PTR_INFO (lhs));
+             if (b != sprime_b)
+               mark_ptr_info_alignment_unknown
+                   (SSA_NAME_PTR_INFO (sprime));
+           }
+         else if (INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+                  && SSA_NAME_RANGE_INFO (lhs)
+                  && ! SSA_NAME_RANGE_INFO (sprime)
+                  && b == sprime_b)
+           duplicate_ssa_name_range_info (sprime,
+                                          SSA_NAME_RANGE_TYPE (lhs),
+                                          SSA_NAME_RANGE_INFO (lhs));
+       }
 
-      switch (TREE_CODE (SSA_NAME_VAR (name)))
+      /* Inhibit the use of an inserted PHI on a loop header when
+        the address of the memory reference is a simple induction
+        variable.  In other cases the vectorizer won't do anything
+        anyway (either it's loop invariant or a complicated
+        expression).  */
+      if (sprime
+         && TREE_CODE (sprime) == SSA_NAME
+         && do_pre
+         && (flag_tree_loop_vectorize || flag_tree_parallelize_loops > 1)
+         && loop_outer (b->loop_father)
+         && has_zero_uses (sprime)
+         && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime))
+         && gimple_assign_load_p (stmt))
        {
-       case VAR_DECL:
-         /* All undefined vars are VARYING.  */
-         VN_INFO (name)->valnum = name; 
-         VN_INFO (name)->visited = true;
-         break;
+         gimple *def_stmt = SSA_NAME_DEF_STMT (sprime);
+         basic_block def_bb = gimple_bb (def_stmt);
+         if (gimple_code (def_stmt) == GIMPLE_PHI
+             && def_bb->loop_father->header == def_bb)
+           {
+             loop_p loop = def_bb->loop_father;
+             ssa_op_iter iter;
+             tree op;
+             bool found = false;
+             FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
+               {
+                 affine_iv iv;
+                 def_bb = gimple_bb (SSA_NAME_DEF_STMT (op));
+                 if (def_bb
+                     && flow_bb_inside_loop_p (loop, def_bb)
+                     && simple_iv (loop, loop, op, &iv, true))
+                   {
+                     found = true;
+                     break;
+                   }
+               }
+             if (found)
+               {
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   {
+                     fprintf (dump_file, "Not replacing ");
+                     print_gimple_expr (dump_file, stmt, 0);
+                     fprintf (dump_file, " with ");
+                     print_generic_expr (dump_file, sprime);
+                     fprintf (dump_file, " which would add a loop"
+                              " carried dependence to loop %d\n",
+                              loop->num);
+                   }
+                 /* Don't keep sprime available.  */
+                 sprime = NULL_TREE;
+               }
+           }
+       }
 
-       case PARM_DECL:
-         /* Parameters are VARYING but we can record a condition
-            if we know it is a non-NULL pointer.  */
-         VN_INFO (name)->visited = true;
-         VN_INFO (name)->valnum = name; 
-         if (POINTER_TYPE_P (TREE_TYPE (name))
-             && nonnull_arg_p (SSA_NAME_VAR (name)))
+      if (sprime)
+       {
+         /* If we can propagate the value computed for LHS into
+            all uses don't bother doing anything with this stmt.  */
+         if (may_propagate_copy (lhs, sprime))
            {
-             tree ops[2];
-             ops[0] = name;
-             ops[1] = build_int_cst (TREE_TYPE (name), 0);
-             vn_nary_op_insert_pieces (2, NE_EXPR, boolean_type_node, ops,
-                                       boolean_true_node, 0);
+             /* Mark it for removal.  */
+             to_remove.safe_push (stmt);
+
+             /* ???  Don't count copy/constant propagations.  */
+             if (gimple_assign_single_p (stmt)
+                 && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
+                     || gimple_assign_rhs1 (stmt) == sprime))
+               return;
+
              if (dump_file && (dump_flags & TDF_DETAILS))
                {
-                 fprintf (dump_file, "Recording ");
-                 print_generic_expr (dump_file, name, TDF_SLIM);
-                 fprintf (dump_file, " != 0\n");
+                 fprintf (dump_file, "Replaced ");
+                 print_gimple_expr (dump_file, stmt, 0);
+                 fprintf (dump_file, " with ");
+                 print_generic_expr (dump_file, sprime);
+                 fprintf (dump_file, " in all uses of ");
+                 print_gimple_stmt (dump_file, stmt, 0);
                }
+
+             eliminations++;
+             return;
            }
-         break;
 
-       case RESULT_DECL:
-         /* If the result is passed by invisible reference the default
-            def is initialized, otherwise it's uninitialized.  Still
-            undefined is varying.  */
-         VN_INFO (name)->visited = true;
-         VN_INFO (name)->valnum = name; 
-         break;
+         /* If this is an assignment from our leader (which
+            happens in the case the value-number is a constant)
+            then there is nothing to do.  */
+         if (gimple_assign_single_p (stmt)
+             && sprime == gimple_assign_rhs1 (stmt))
+           return;
 
-       default:
-         gcc_unreachable ();
+         /* Else replace its RHS.  */
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Replaced ");
+             print_gimple_expr (dump_file, stmt, 0);
+             fprintf (dump_file, " with ");
+             print_generic_expr (dump_file, sprime);
+             fprintf (dump_file, " in ");
+             print_gimple_stmt (dump_file, stmt, 0);
+           }
+         eliminations++;
+
+         bool can_make_abnormal_goto = (is_gimple_call (stmt)
+                                        && stmt_can_make_abnormal_goto (stmt));
+         gimple *orig_stmt = stmt;
+         if (!useless_type_conversion_p (TREE_TYPE (lhs),
+                                         TREE_TYPE (sprime)))
+           {
+             /* We preserve conversions to but not from function or method
+                types.  This asymmetry makes it necessary to re-instantiate
+                conversions here.  */
+             if (POINTER_TYPE_P (TREE_TYPE (lhs))
+                 && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (lhs))))
+               sprime = fold_convert (TREE_TYPE (lhs), sprime);
+             else
+               gcc_unreachable ();
+           }
+         tree vdef = gimple_vdef (stmt);
+         tree vuse = gimple_vuse (stmt);
+         propagate_tree_value_into_stmt (gsi, sprime);
+         stmt = gsi_stmt (*gsi);
+         update_stmt (stmt);
+         /* In case the VDEF on the original stmt was released, value-number
+            it to the VUSE.  This is to make vuse_ssa_val able to skip
+            released virtual operands.  */
+         if (vdef != gimple_vdef (stmt))
+           {
+             gcc_assert (SSA_NAME_IN_FREE_LIST (vdef));
+             VN_INFO (vdef)->valnum = vuse;
+           }
+
+         /* If we removed EH side-effects from the statement, clean
+            its EH information.  */
+         if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt))
+           {
+             bitmap_set_bit (need_eh_cleanup,
+                             gimple_bb (stmt)->index);
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file, "  Removed EH side-effects.\n");
+           }
+
+         /* Likewise for AB side-effects.  */
+         if (can_make_abnormal_goto
+             && !stmt_can_make_abnormal_goto (stmt))
+           {
+             bitmap_set_bit (need_ab_cleanup,
+                             gimple_bb (stmt)->index);
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file, "  Removed AB side-effects.\n");
+           }
+
+         return;
        }
     }
-}
 
-/* Restore SSA info that has been reset on value leaders.  */
+  /* If the statement is a scalar store, see if the expression
+     has the same value number as its rhs.  If so, the store is
+     dead.  */
+  if (gimple_assign_single_p (stmt)
+      && !gimple_has_volatile_ops (stmt)
+      && !is_gimple_reg (gimple_assign_lhs (stmt))
+      && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
+         || is_gimple_min_invariant (gimple_assign_rhs1 (stmt))))
+    {
+      tree val;
+      tree rhs = gimple_assign_rhs1 (stmt);
+      vn_reference_t vnresult;
+      val = vn_reference_lookup (lhs, gimple_vuse (stmt), VN_WALKREWRITE,
+                                &vnresult, false);
+      if (TREE_CODE (rhs) == SSA_NAME)
+       rhs = VN_INFO (rhs)->valnum;
+      if (val
+         && operand_equal_p (val, rhs, 0))
+       {
+         /* We can only remove the later store if the former aliases
+            at least all accesses the later one does or if the store
+            was to readonly memory storing the same value.  */
+         alias_set_type set = get_alias_set (lhs);
+         if (! vnresult
+             || vnresult->set == set
+             || alias_set_subset_of (set, vnresult->set))
+           {
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "Deleted redundant store ");
+                 print_gimple_stmt (dump_file, stmt, 0);
+               }
 
-void
-scc_vn_restore_ssa_info (void)
-{
-  unsigned i;
-  tree name;
+             /* Queue stmt for removal.  */
+             to_remove.safe_push (stmt);
+             return;
+           }
+       }
+    }
 
-  FOR_EACH_SSA_NAME (i, name, cfun)
+  /* If this is a control statement value numbering left edges
+     unexecuted on force the condition in a way consistent with
+     that.  */
+  if (gcond *cond = dyn_cast <gcond *> (stmt))
     {
-      if (has_VN_INFO (name))
+      if ((EDGE_SUCC (b, 0)->flags & EDGE_EXECUTABLE)
+         ^ (EDGE_SUCC (b, 1)->flags & EDGE_EXECUTABLE))
        {
-         if (VN_INFO (name)->needs_insertion)
-           ;
-         else if (POINTER_TYPE_P (TREE_TYPE (name))
-                  && VN_INFO (name)->info.ptr_info)
-           SSA_NAME_PTR_INFO (name) = VN_INFO (name)->info.ptr_info;
-         else if (INTEGRAL_TYPE_P (TREE_TYPE (name))
-                  && VN_INFO (name)->info.range_info)
+         if (dump_file && (dump_flags & TDF_DETAILS))
            {
-             SSA_NAME_RANGE_INFO (name) = VN_INFO (name)->info.range_info;
-             SSA_NAME_ANTI_RANGE_P (name)
-               = VN_INFO (name)->range_info_anti_range_p;
+             fprintf (dump_file, "Removing unexecutable edge from ");
+             print_gimple_stmt (dump_file, stmt, 0);
            }
+         if (((EDGE_SUCC (b, 0)->flags & EDGE_TRUE_VALUE) != 0)
+             == ((EDGE_SUCC (b, 0)->flags & EDGE_EXECUTABLE) != 0))
+           gimple_cond_make_true (cond);
+         else
+           gimple_cond_make_false (cond);
+         update_stmt (cond);
+         el_todo |= TODO_cleanup_cfg;
+         return;
        }
     }
-}
 
-void
-free_scc_vn (void)
-{
-  size_t i;
-  tree name;
-
-  delete constant_to_value_id;
-  constant_to_value_id = NULL;
-  BITMAP_FREE (constant_value_ids);
-  shared_lookup_phiargs.release ();
-  shared_lookup_references.release ();
-  XDELETEVEC (rpo_numbers);
+  bool can_make_abnormal_goto = stmt_can_make_abnormal_goto (stmt);
+  bool was_noreturn = (is_gimple_call (stmt)
+                      && gimple_call_noreturn_p (stmt));
+  tree vdef = gimple_vdef (stmt);
+  tree vuse = gimple_vuse (stmt);
 
-  FOR_EACH_SSA_NAME (i, name, cfun)
+  /* If we didn't replace the whole stmt (or propagate the result
+     into all uses), replace all uses on this stmt with their
+     leaders.  */
+  bool modified = false;
+  use_operand_p use_p;
+  ssa_op_iter iter;
+  FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
     {
-      if (has_VN_INFO (name)
-         && VN_INFO (name)->needs_insertion)
-       release_ssa_name (name);
+      tree use = USE_FROM_PTR (use_p);
+      /* ???  The call code above leaves stmt operands un-updated.  */
+      if (TREE_CODE (use) != SSA_NAME)
+       continue;
+      tree sprime;
+      if (SSA_NAME_IS_DEFAULT_DEF (use))
+       /* ???  For default defs BB shouldn't matter, but we have to
+          solve the inconsistency between rpo eliminate and
+          dom eliminate avail valueization first.  */
+       sprime = eliminate_avail (b, use);
+      else
+       /* Look for sth available at the definition block of the argument.
+          This avoids inconsistencies between availability there which
+          decides if the stmt can be removed and availability at the
+          use site.  The SSA property ensures that things available
+          at the definition are also available at uses.  */
+       sprime = eliminate_avail (gimple_bb (SSA_NAME_DEF_STMT (use)), use);
+      if (sprime && sprime != use
+         && may_propagate_copy (use, sprime)
+         /* We substitute into debug stmts to avoid excessive
+            debug temporaries created by removed stmts, but we need
+            to avoid doing so for inserted sprimes as we never want
+            to create debug temporaries for them.  */
+         && (!inserted_exprs
+             || TREE_CODE (sprime) != SSA_NAME
+             || !is_gimple_debug (stmt)
+             || !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime))))
+       {
+         propagate_value (use_p, sprime);
+         modified = true;
+       }
     }
-  obstack_free (&vn_ssa_aux_obstack, NULL);
-  vn_ssa_aux_table.release ();
 
-  sccstack.release ();
-  free_vn_table (valid_info);
-  XDELETE (valid_info);
-  free_vn_table (optimistic_info);
-  XDELETE (optimistic_info);
+  /* Fold the stmt if modified, this canonicalizes MEM_REFs we propagated
+     into which is a requirement for the IPA devirt machinery.  */
+  gimple *old_stmt = stmt;
+  if (modified)
+    {
+      /* If a formerly non-invariant ADDR_EXPR is turned into an
+        invariant one it was on a separate stmt.  */
+      if (gimple_assign_single_p (stmt)
+         && TREE_CODE (gimple_assign_rhs1 (stmt)) == ADDR_EXPR)
+       recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt));
+      gimple_stmt_iterator prev = *gsi;
+      gsi_prev (&prev);
+      if (fold_stmt (gsi))
+       {
+         /* fold_stmt may have created new stmts inbetween
+            the previous stmt and the folded stmt.  Mark
+            all defs created there as varying to not confuse
+            the SCCVN machinery as we're using that even during
+            elimination.  */
+         if (gsi_end_p (prev))
+           prev = gsi_start_bb (b);
+         else
+           gsi_next (&prev);
+         if (gsi_stmt (prev) != gsi_stmt (*gsi))
+           do
+             {
+               tree def;
+               ssa_op_iter dit;
+               FOR_EACH_SSA_TREE_OPERAND (def, gsi_stmt (prev),
+                                          dit, SSA_OP_ALL_DEFS)
+                   /* As existing DEFs may move between stmts
+                      only process new ones.  */
+                   if (! has_VN_INFO (def))
+                     {
+                       VN_INFO (def)->valnum = def;
+                       VN_INFO (def)->visited = true;
+                     }
+               if (gsi_stmt (prev) == gsi_stmt (*gsi))
+                 break;
+               gsi_next (&prev);
+             }
+           while (1);
+       }
+      stmt = gsi_stmt (*gsi);
+      /* In case we folded the stmt away schedule the NOP for removal.  */
+      if (gimple_nop_p (stmt))
+       to_remove.safe_push (stmt);
+    }
 
-  BITMAP_FREE (const_parms);
-}
+  /* Visit indirect calls and turn them into direct calls if
+     possible using the devirtualization machinery.  Do this before
+     checking for required EH/abnormal/noreturn cleanup as devird
+     may expose more of those.  */
+  if (gcall *call_stmt = dyn_cast <gcall *> (stmt))
+    {
+      tree fn = gimple_call_fn (call_stmt);
+      if (fn
+         && flag_devirtualize
+         && virtual_method_call_p (fn))
+       {
+         tree otr_type = obj_type_ref_class (fn);
+         unsigned HOST_WIDE_INT otr_tok
+             = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (fn));
+         tree instance;
+         ipa_polymorphic_call_context context (current_function_decl,
+                                               fn, stmt, &instance);
+         context.get_dynamic_type (instance, OBJ_TYPE_REF_OBJECT (fn),
+                                   otr_type, stmt, NULL);
+         bool final;
+         vec <cgraph_node *> targets
+             = possible_polymorphic_call_targets (obj_type_ref_class (fn),
+                                                  otr_tok, context, &final);
+         if (dump_file)
+           dump_possible_polymorphic_call_targets (dump_file,
+                                                   obj_type_ref_class (fn),
+                                                   otr_tok, context);
+         if (final && targets.length () <= 1 && dbg_cnt (devirt))
+           {
+             tree fn;
+             if (targets.length () == 1)
+               fn = targets[0]->decl;
+             else
+               fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+             if (dump_enabled_p ())
+               {
+                 dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+                                  "converting indirect call to "
+                                  "function %s\n",
+                                  lang_hooks.decl_printable_name (fn, 2));
+               }
+             gimple_call_set_fndecl (call_stmt, fn);
+             /* If changing the call to __builtin_unreachable
+                or similar noreturn function, adjust gimple_call_fntype
+                too.  */
+             if (gimple_call_noreturn_p (call_stmt)
+                 && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fn)))
+                 && TYPE_ARG_TYPES (TREE_TYPE (fn))
+                 && (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn)))
+                     == void_type_node))
+               gimple_call_set_fntype (call_stmt, TREE_TYPE (fn));
+             maybe_remove_unused_call_args (cfun, call_stmt);
+             modified = true;
+           }
+       }
+    }
 
-/* Set *ID according to RESULT.  */
+  if (modified)
+    {
+      /* When changing a call into a noreturn call, cfg cleanup
+        is needed to fix up the noreturn call.  */
+      if (!was_noreturn
+         && is_gimple_call (stmt) && gimple_call_noreturn_p (stmt))
+       to_fixup.safe_push  (stmt);
+      /* When changing a condition or switch into one we know what
+        edge will be executed, schedule a cfg cleanup.  */
+      if ((gimple_code (stmt) == GIMPLE_COND
+          && (gimple_cond_true_p (as_a <gcond *> (stmt))
+              || gimple_cond_false_p (as_a <gcond *> (stmt))))
+         || (gimple_code (stmt) == GIMPLE_SWITCH
+             && TREE_CODE (gimple_switch_index
+                           (as_a <gswitch *> (stmt))) == INTEGER_CST))
+       el_todo |= TODO_cleanup_cfg;
+      /* If we removed EH side-effects from the statement, clean
+        its EH information.  */
+      if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
+       {
+         bitmap_set_bit (need_eh_cleanup,
+                         gimple_bb (stmt)->index);
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "  Removed EH side-effects.\n");
+       }
+      /* Likewise for AB side-effects.  */
+      if (can_make_abnormal_goto
+         && !stmt_can_make_abnormal_goto (stmt))
+       {
+         bitmap_set_bit (need_ab_cleanup,
+                         gimple_bb (stmt)->index);
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "  Removed AB side-effects.\n");
+       }
+      update_stmt (stmt);
+      /* In case the VDEF on the original stmt was released, value-number
+         it to the VUSE.  This is to make vuse_ssa_val able to skip
+        released virtual operands.  */
+      if (vdef && SSA_NAME_IN_FREE_LIST (vdef))
+       VN_INFO (vdef)->valnum = vuse;
+    }
 
-static void
-set_value_id_for_result (tree result, unsigned int *id)
-{
-  if (result && TREE_CODE (result) == SSA_NAME)
-    *id = VN_INFO (result)->value_id;
-  else if (result && is_gimple_min_invariant (result))
-    *id = get_or_alloc_constant_value_id (result);
-  else
-    *id = get_next_value_id ();
+  /* Make new values available - for fully redundant LHS we
+     continue with the next stmt above and skip this.  */
+  def_operand_p defp;
+  FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF)
+    eliminate_push_avail (b, DEF_FROM_PTR (defp));
 }
 
-/* Set the value ids in the valid hash tables.  */
+/* Perform elimination for the basic-block B during the domwalk.  */
 
-static void
-set_hashtable_value_ids (void)
+edge
+eliminate_dom_walker::before_dom_children (basic_block b)
 {
-  vn_nary_op_iterator_type hin;
-  vn_phi_iterator_type hip;
-  vn_reference_iterator_type hir;
-  vn_nary_op_t vno;
-  vn_reference_t vr;
-  vn_phi_t vp;
+  /* Mark new bb.  */
+  avail_stack.safe_push (NULL_TREE);
 
-  /* Now set the value ids of the things we had put in the hash
-     table.  */
+  /* Skip unreachable blocks marked unreachable during the SCCVN domwalk.  */
+  if (!(b->flags & BB_EXECUTABLE))
+    return NULL;
 
-  FOR_EACH_HASH_TABLE_ELEMENT (*valid_info->nary, vno, vn_nary_op_t, hin)
-    set_value_id_for_result (vno->result, &vno->value_id);
+  vn_context_bb = b;
 
-  FOR_EACH_HASH_TABLE_ELEMENT (*valid_info->phis, vp, vn_phi_t, hip)
-    set_value_id_for_result (vp->result, &vp->value_id);
+  for (gphi_iterator gsi = gsi_start_phis (b); !gsi_end_p (gsi);)
+    {
+      gphi *phi = gsi.phi ();
+      tree res = PHI_RESULT (phi);
 
-  FOR_EACH_HASH_TABLE_ELEMENT (*valid_info->references, vr, vn_reference_t,
-                              hir)
-    set_value_id_for_result (vr->result, &vr->value_id);
-}
+      if (virtual_operand_p (res))
+       {
+         gsi_next (&gsi);
+         continue;
+       }
 
-class sccvn_dom_walker : public dom_walker
-{
-public:
-  sccvn_dom_walker ()
-    : dom_walker (CDI_DOMINATORS, REACHABLE_BLOCKS), cond_stack (0) {}
+      tree sprime = eliminate_avail (b, res);
+      if (sprime
+         && sprime != res)
+       {
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Replaced redundant PHI node defining ");
+             print_generic_expr (dump_file, res);
+             fprintf (dump_file, " with ");
+             print_generic_expr (dump_file, sprime);
+             fprintf (dump_file, "\n");
+           }
 
-  virtual edge before_dom_children (basic_block);
-  virtual void after_dom_children (basic_block);
+         /* If we inserted this PHI node ourself, it's not an elimination.  */
+         if (! inserted_exprs
+             || ! bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res)))
+           eliminations++;
 
-  void record_cond (basic_block,
-                   enum tree_code code, tree lhs, tree rhs, bool value);
-  void record_conds (basic_block,
-                    enum tree_code code, tree lhs, tree rhs, bool value);
+         /* If we will propagate into all uses don't bother to do
+            anything.  */
+         if (may_propagate_copy (res, sprime))
+           {
+             /* Mark the PHI for removal.  */
+             to_remove.safe_push (phi);
+             gsi_next (&gsi);
+             continue;
+           }
 
-  auto_vec<std::pair <basic_block, std::pair <vn_nary_op_t, vn_nary_op_t> > >
-    cond_stack;
-};
+         remove_phi_node (&gsi, false);
 
-/* Record a temporary condition for the BB and its dominated blocks.  */
+         if (!useless_type_conversion_p (TREE_TYPE (res), TREE_TYPE (sprime)))
+           sprime = fold_convert (TREE_TYPE (res), sprime);
+         gimple *stmt = gimple_build_assign (res, sprime);
+         gimple_stmt_iterator gsi2 = gsi_after_labels (b);
+         gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT);
+         continue;
+       }
 
-void
-sccvn_dom_walker::record_cond (basic_block bb,
-                              enum tree_code code, tree lhs, tree rhs,
-                              bool value)
-{
-  tree ops[2] = { lhs, rhs };
-  vn_nary_op_t old = NULL;
-  if (vn_nary_op_lookup_pieces (2, code, boolean_type_node, ops, &old))
-    current_info->nary->remove_elt_with_hash (old, old->hashcode);
-  vn_nary_op_t cond
-    = vn_nary_op_insert_pieces (2, code, boolean_type_node, ops,
-                               value
-                               ? boolean_true_node
-                               : boolean_false_node, 0);
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      fprintf (dump_file, "Recording temporarily ");
-      print_generic_expr (dump_file, ops[0], TDF_SLIM);
-      fprintf (dump_file, " %s ", get_tree_code_name (code));
-      print_generic_expr (dump_file, ops[1], TDF_SLIM);
-      fprintf (dump_file, " == %s%s\n",
-              value ? "true" : "false",
-              old ? " (old entry saved)" : "");
+      eliminate_push_avail (b, res);
+      gsi_next (&gsi);
     }
-  cond_stack.safe_push (std::make_pair (bb, std::make_pair (cond, old)));
-}
-
-/* Record temporary conditions for the BB and its dominated blocks
-   according to LHS CODE RHS == VALUE and its dominated conditions.  */
-
-void
-sccvn_dom_walker::record_conds (basic_block bb,
-                               enum tree_code code, tree lhs, tree rhs,
-                               bool value)
-{
-  /* Record the original condition.  */
-  record_cond (bb, code, lhs, rhs, value);
 
-  if (!value)
-    return;
+  for (gimple_stmt_iterator gsi = gsi_start_bb (b);
+       !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    eliminate_stmt (b, &gsi);
 
-  /* Record dominated conditions if the condition is true.  Note that
-     the inversion is already recorded.  */
-  switch (code)
-    {
-    case LT_EXPR:
-    case GT_EXPR:
-      record_cond (bb, code == LT_EXPR ? LE_EXPR : GE_EXPR, lhs, rhs, true);
-      record_cond (bb, NE_EXPR, lhs, rhs, true);
-      record_cond (bb, EQ_EXPR, lhs, rhs, false);
-      break;
+  /* Replace destination PHI arguments.  */
+  edge_iterator ei;
+  edge e;
+  FOR_EACH_EDGE (e, ei, b->succs)
+    if (e->flags & EDGE_EXECUTABLE)
+      for (gphi_iterator gsi = gsi_start_phis (e->dest);
+          !gsi_end_p (gsi);
+          gsi_next (&gsi))
+       {
+         gphi *phi = gsi.phi ();
+         use_operand_p use_p = PHI_ARG_DEF_PTR_FROM_EDGE (phi, e);
+         tree arg = USE_FROM_PTR (use_p);
+         if (TREE_CODE (arg) != SSA_NAME
+             || virtual_operand_p (arg))
+           continue;
+         tree sprime = eliminate_avail (b, arg);
+         if (sprime && may_propagate_copy (arg, sprime))
+           propagate_value (use_p, sprime);
+       }
 
-    case EQ_EXPR:
-      record_cond (bb, LE_EXPR, lhs, rhs, true);
-      record_cond (bb, GE_EXPR, lhs, rhs, true);
-      record_cond (bb, LT_EXPR, lhs, rhs, false);
-      record_cond (bb, GT_EXPR, lhs, rhs, false);
-      break;
+  vn_context_bb = NULL;
 
-    default:
-      break;
-    }
+  return NULL;
 }
-/* Restore expressions and values derived from conditionals.  */
+
+/* Make no longer available leaders no longer available.  */
 
 void
-sccvn_dom_walker::after_dom_children (basic_block bb)
+eliminate_dom_walker::after_dom_children (basic_block)
 {
-  while (!cond_stack.is_empty ()
-        && cond_stack.last ().first == bb)
+  tree entry;
+  while ((entry = avail_stack.pop ()) != NULL_TREE)
     {
-      vn_nary_op_t cond = cond_stack.last ().second.first;
-      vn_nary_op_t old = cond_stack.last ().second.second;
-      current_info->nary->remove_elt_with_hash (cond, cond->hashcode);
-      if (old)
-       vn_nary_op_insert_into (old, current_info->nary, false);
-      cond_stack.pop ();
+      tree valnum = VN_INFO (entry)->valnum;
+      tree old = avail[SSA_NAME_VERSION (valnum)];
+      if (old == entry)
+       avail[SSA_NAME_VERSION (valnum)] = NULL_TREE;
+      else
+       avail[SSA_NAME_VERSION (valnum)] = entry;
     }
 }
 
-/* Value number all statements in BB.  */
+/* Remove queued stmts and perform delayed cleanups.  */
 
-edge
-sccvn_dom_walker::before_dom_children (basic_block bb)
+unsigned
+eliminate_dom_walker::eliminate_cleanup (bool region_p)
 {
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "Visiting BB %d\n", bb->index);
+  statistics_counter_event (cfun, "Eliminated", eliminations);
+  statistics_counter_event (cfun, "Insertions", insertions);
 
-  /* If we have a single predecessor record the equivalence from a
-     possible condition on the predecessor edge.  */
-  edge pred_e = single_pred_edge_ignoring_loop_edges (bb, false);
-  if (pred_e)
+  /* We cannot remove stmts during BB walk, especially not release SSA
+     names there as this confuses the VN machinery.  The stmts ending
+     up in to_remove are either stores or simple copies.
+     Remove stmts in reverse order to make debug stmt creation possible.  */
+  while (!to_remove.is_empty ())
     {
-      /* Check if there are multiple executable successor edges in
-        the source block.  Otherwise there is no additional info
-        to be recorded.  */
-      edge_iterator ei;
-      edge e2;
-      FOR_EACH_EDGE (e2, ei, pred_e->src->succs)
-       if (e2 != pred_e
-           && e2->flags & EDGE_EXECUTABLE)
-         break;
-      if (e2 && (e2->flags & EDGE_EXECUTABLE))
+      bool do_release_defs = true;
+      gimple *stmt = to_remove.pop ();
+
+      /* When we are value-numbering a region we do not require exit PHIs to
+        be present so we have to make sure to deal with uses outside of the
+        region of stmts that we thought are eliminated.
+        ??? Note we may be confused by uses in dead regions we didn't run
+        elimination on.  Rather than checking individual uses we accept
+        dead copies to be generated here (gcc.c-torture/execute/20060905-1.c
+        contains such example).  */
+      if (region_p)
        {
-         gimple *stmt = last_stmt (pred_e->src);
-         if (stmt
-             && gimple_code (stmt) == GIMPLE_COND)
+         if (gphi *phi = dyn_cast <gphi *> (stmt))
            {
-             enum tree_code code = gimple_cond_code (stmt);
-             tree lhs = gimple_cond_lhs (stmt);
-             tree rhs = gimple_cond_rhs (stmt);
-             record_conds (bb, code, lhs, rhs,
-                           (pred_e->flags & EDGE_TRUE_VALUE) != 0);
-             code = invert_tree_comparison (code, HONOR_NANS (lhs));
-             if (code != ERROR_MARK)
-               record_conds (bb, code, lhs, rhs,
-                             (pred_e->flags & EDGE_TRUE_VALUE) == 0);
+             tree lhs = gimple_phi_result (phi);
+             if (!has_zero_uses (lhs))
+               {
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   fprintf (dump_file, "Keeping eliminated stmt live "
+                            "as copy because of out-of-region uses\n");
+                 tree sprime = eliminate_avail (gimple_bb (stmt), lhs);
+                 gimple *copy = gimple_build_assign (lhs, sprime);
+                 gimple_stmt_iterator gsi
+                   = gsi_after_labels (gimple_bb (stmt));
+                 gsi_insert_before (&gsi, copy, GSI_SAME_STMT);
+                 do_release_defs = false;
+               }
            }
+         else if (tree lhs = gimple_get_lhs (stmt))
+           if (TREE_CODE (lhs) == SSA_NAME
+               && !has_zero_uses (lhs))
+             {
+               if (dump_file && (dump_flags & TDF_DETAILS))
+                 fprintf (dump_file, "Keeping eliminated stmt live "
+                          "as copy because of out-of-region uses\n");
+               tree sprime = eliminate_avail (gimple_bb (stmt), lhs);
+               gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+               if (is_gimple_assign (stmt))
+                 {
+                   gimple_assign_set_rhs_from_tree (&gsi, sprime);
+                   stmt = gsi_stmt (gsi);
+                   update_stmt (stmt);
+                   if (maybe_clean_or_replace_eh_stmt (stmt, stmt))
+                     bitmap_set_bit (need_eh_cleanup, gimple_bb (stmt)->index);
+                   continue;
+                 }
+               else
+                 {
+                   gimple *copy = gimple_build_assign (lhs, sprime);
+                   gsi_insert_before (&gsi, copy, GSI_SAME_STMT);
+                   do_release_defs = false;
+                 }
+             }
        }
-    }
-
-  /* Value-number all defs in the basic-block.  */
-  for (gphi_iterator gsi = gsi_start_phis (bb);
-       !gsi_end_p (gsi); gsi_next (&gsi))
-    {
-      gphi *phi = gsi.phi ();
-      tree res = PHI_RESULT (phi);
-      if (!VN_INFO (res)->visited)
-       DFS (res);
-    }
-  for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
-       !gsi_end_p (gsi); gsi_next (&gsi))
-    {
-      ssa_op_iter i;
-      tree op;
-      FOR_EACH_SSA_TREE_OPERAND (op, gsi_stmt (gsi), i, SSA_OP_ALL_DEFS)
-       if (!VN_INFO (op)->visited)
-         DFS (op);
-    }
 
-  /* Finally look at the last stmt.  */
-  gimple *stmt = last_stmt (bb);
-  if (!stmt)
-    return NULL;
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Removing dead stmt ");
+         print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
+       }
 
-  enum gimple_code code = gimple_code (stmt);
-  if (code != GIMPLE_COND
-      && code != GIMPLE_SWITCH
-      && code != GIMPLE_GOTO)
-    return NULL;
+      gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+      if (gimple_code (stmt) == GIMPLE_PHI)
+       remove_phi_node (&gsi, do_release_defs);
+      else
+       {
+         basic_block bb = gimple_bb (stmt);
+         unlink_stmt_vdef (stmt);
+         if (gsi_remove (&gsi, true))
+           bitmap_set_bit (need_eh_cleanup, bb->index);
+         if (is_gimple_call (stmt) && stmt_can_make_abnormal_goto (stmt))
+           bitmap_set_bit (need_ab_cleanup, bb->index);
+         if (do_release_defs)
+           release_defs (stmt);
+       }
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      fprintf (dump_file, "Visiting control stmt ending BB %d: ", bb->index);
-      print_gimple_stmt (dump_file, stmt, 0);
+      /* Removing a stmt may expose a forwarder block.  */
+      el_todo |= TODO_cleanup_cfg;
     }
 
-  /* ???  We can even handle stmts with outgoing EH or ABNORMAL edges
-     if value-numbering can prove they are not reachable.  Handling
-     computed gotos is also possible.  */
-  tree val;
-  switch (code)
+  /* Fixup stmts that became noreturn calls.  This may require splitting
+     blocks and thus isn't possible during the dominator walk.  Do this
+     in reverse order so we don't inadvertedly remove a stmt we want to
+     fixup by visiting a dominating now noreturn call first.  */
+  while (!to_fixup.is_empty ())
     {
-    case GIMPLE_COND:
-      {
-       tree lhs = vn_valueize (gimple_cond_lhs (stmt));
-       tree rhs = vn_valueize (gimple_cond_rhs (stmt));
-       val = gimple_simplify (gimple_cond_code (stmt),
-                              boolean_type_node, lhs, rhs,
-                              NULL, vn_valueize);
-       /* If that didn't simplify to a constant see if we have recorded
-          temporary expressions from taken edges.  */
-       if (!val || TREE_CODE (val) != INTEGER_CST)
-         {
-           tree ops[2];
-           ops[0] = lhs;
-           ops[1] = rhs;
-           val = vn_nary_op_lookup_pieces (2, gimple_cond_code (stmt),
-                                           boolean_type_node, ops, NULL);
-         }
-       break;
-      }
-    case GIMPLE_SWITCH:
-      val = gimple_switch_index (as_a <gswitch *> (stmt));
-      break;
-    case GIMPLE_GOTO:
-      val = gimple_goto_dest (stmt);
-      break;
-    default:
-      gcc_unreachable ();
+      gimple *stmt = to_fixup.pop ();
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Fixing up noreturn call ");
+         print_gimple_stmt (dump_file, stmt, 0);
+       }
+
+      if (fixup_noreturn_call (stmt))
+       el_todo |= TODO_cleanup_cfg;
     }
-  if (!val)
-    return NULL;
 
-  edge taken = find_taken_edge (bb, vn_valueize (val));
-  if (!taken)
-    return NULL;
+  bool do_eh_cleanup = !bitmap_empty_p (need_eh_cleanup);
+  bool do_ab_cleanup = !bitmap_empty_p (need_ab_cleanup);
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "Marking all edges out of BB %d but (%d -> %d) as "
-            "not executable\n", bb->index, bb->index, taken->dest->index);
+  if (do_eh_cleanup)
+    gimple_purge_all_dead_eh_edges (need_eh_cleanup);
+
+  if (do_ab_cleanup)
+    gimple_purge_all_dead_abnormal_call_edges (need_ab_cleanup);
 
-  return taken;
+  if (do_eh_cleanup || do_ab_cleanup)
+    el_todo |= TODO_cleanup_cfg;
+
+  return el_todo;
 }
 
-/* Do SCCVN.  Returns true if it finished, false if we bailed out
-   due to resource constraints.  DEFAULT_VN_WALK_KIND_ specifies
-   how we use the alias oracle walking during the VN process.  */
+/* Eliminate fully redundant computations.  */
 
-void
-run_scc_vn (vn_lookup_kind default_vn_walk_kind_)
+unsigned
+eliminate_with_rpo_vn (bitmap inserted_exprs)
 {
-  size_t i;
+  eliminate_dom_walker walker (CDI_DOMINATORS, inserted_exprs);
 
-  default_vn_walk_kind = default_vn_walk_kind_;
+  walker.walk (cfun->cfg->x_entry_block_ptr);
+  return walker.eliminate_cleanup ();
+}
 
-  init_scc_vn ();
+static unsigned
+do_rpo_vn (function *fn, edge entry, bitmap exit_bbs,
+          bool iterate, bool eliminate);
 
-  /* Collect pointers we know point to readonly memory.  */
-  const_parms = BITMAP_ALLOC (NULL);
-  tree fnspec = lookup_attribute ("fn spec",
-                                 TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl)));
-  if (fnspec)
-    {
-      fnspec = TREE_VALUE (TREE_VALUE (fnspec));
-      i = 1;
-      for (tree arg = DECL_ARGUMENTS (cfun->decl);
-          arg; arg = DECL_CHAIN (arg), ++i)
-       {
-         if (i >= (unsigned) TREE_STRING_LENGTH (fnspec))
-           break;
-         if (TREE_STRING_POINTER (fnspec)[i]  == 'R'
-             || TREE_STRING_POINTER (fnspec)[i] == 'r')
-           {
-             tree name = ssa_default_def (cfun, arg);
-             if (name)
-               bitmap_set_bit (const_parms, SSA_NAME_VERSION (name));
-           }
-       }
-    }
+void
+run_rpo_vn (vn_lookup_kind kind)
+{
+  default_vn_walk_kind = kind;
+  do_rpo_vn (cfun, NULL, NULL, true, false);
 
-  /* Walk all blocks in dominator order, value-numbering stmts
-     SSA defs and decide whether outgoing edges are not executable.  */
-  sccvn_dom_walker walker;
-  walker.walk (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+  /* ???  Prune requirement of these.  */
+  constant_to_value_id = new hash_table<vn_constant_hasher> (23);
+  constant_value_ids = BITMAP_ALLOC (NULL);
 
   /* Initialize the value ids and prune out remaining VN_TOPs
      from dead code.  */
   tree name;
+  unsigned i;
   FOR_EACH_SSA_NAME (i, name, cfun)
     {
       vn_ssa_aux_t info = VN_INFO (name);
@@ -5131,803 +6098,554 @@ run_scc_vn (vn_lookup_kind default_vn_walk_kind_)
              print_generic_expr (dump_file, name);
              fprintf (dump_file, " = ");
              print_generic_expr (dump_file, SSA_VAL (name));
-             fprintf (dump_file, "\n");
+             fprintf (dump_file, " (%04d)\n", VN_INFO (name)->value_id);
            }
        }
     }
 }
 
-/* Return the maximum value id we have ever seen.  */
-
-unsigned int
-get_max_value_id (void)
-{
-  return next_value_id;
-}
-
-/* Return the next unique value id.  */
-
-unsigned int
-get_next_value_id (void)
-{
-  return next_value_id++;
-}
-
-
-/* Compare two expressions E1 and E2 and return true if they are equal.  */
-
-bool
-expressions_equal_p (tree e1, tree e2)
-{
-  /* The obvious case.  */
-  if (e1 == e2)
-    return true;
-
-  /* If either one is VN_TOP consider them equal.  */
-  if (e1 == VN_TOP || e2 == VN_TOP)
-    return true;
-
-  /* If only one of them is null, they cannot be equal.  */
-  if (!e1 || !e2)
-    return false;
-
-  /* Now perform the actual comparison.  */
-  if (TREE_CODE (e1) == TREE_CODE (e2)
-      && operand_equal_p (e1, e2, OEP_PURE_SAME))
-    return true;
-
-  return false;
-}
-
+/* Free VN associated data structures.  */
 
-/* Return true if the nary operation NARY may trap.  This is a copy
-   of stmt_could_throw_1_p adjusted to the SCCVN IL.  */
-
-bool
-vn_nary_may_trap (vn_nary_op_t nary)
+void
+free_rpo_vn (void)
 {
-  tree type;
-  tree rhs2 = NULL_TREE;
-  bool honor_nans = false;
-  bool honor_snans = false;
-  bool fp_operation = false;
-  bool honor_trapv = false;
-  bool handled, ret;
-  unsigned i;
-
-  if (TREE_CODE_CLASS (nary->opcode) == tcc_comparison
-      || TREE_CODE_CLASS (nary->opcode) == tcc_unary
-      || TREE_CODE_CLASS (nary->opcode) == tcc_binary)
-    {
-      type = nary->type;
-      fp_operation = FLOAT_TYPE_P (type);
-      if (fp_operation)
-       {
-         honor_nans = flag_trapping_math && !flag_finite_math_only;
-         honor_snans = flag_signaling_nans != 0;
-       }
-      else if (INTEGRAL_TYPE_P (type)
-              && TYPE_OVERFLOW_TRAPS (type))
-       honor_trapv = true;
-    }
-  if (nary->length >= 2)
-    rhs2 = nary->op[1];
-  ret = operation_could_trap_helper_p (nary->opcode, fp_operation,
-                                      honor_trapv,
-                                      honor_nans, honor_snans, rhs2,
-                                      &handled);
-  if (handled
-      && ret)
-    return true;
-
-  for (i = 0; i < nary->length; ++i)
-    if (tree_could_trap_p (nary->op[i]))
-      return true;
+  free_vn_table (valid_info);
+  XDELETE (valid_info);
+  obstack_free (&vn_tables_obstack, NULL);
+  obstack_free (&vn_tables_insert_obstack, NULL);
+
+  vn_ssa_aux_iterator_type it;
+  vn_ssa_aux_t info;
+  FOR_EACH_HASH_TABLE_ELEMENT (*vn_ssa_aux_hash, info, vn_ssa_aux_t, it)
+    if (info->needs_insertion)
+      release_ssa_name (info->name);
+  obstack_free (&vn_ssa_aux_obstack, NULL);
+  delete vn_ssa_aux_hash;
 
-  return false;
+  delete constant_to_value_id;
+  constant_to_value_id = NULL;
+  BITMAP_FREE (constant_value_ids);
 }
 
+/* Hook for maybe_push_res_to_seq, lookup the expression in the VN tables.  */
 
-class eliminate_dom_walker : public dom_walker
-{
-public:
-  eliminate_dom_walker (cdi_direction, bitmap);
-  ~eliminate_dom_walker ();
-
-  virtual edge before_dom_children (basic_block);
-  virtual void after_dom_children (basic_block);
-
-  tree eliminate_avail (tree op);
-  void eliminate_push_avail (tree op);
-  tree eliminate_insert (gimple_stmt_iterator *gsi, tree val);
-
-  bool do_pre;
-  unsigned int el_todo;
-  unsigned int eliminations;
-  unsigned int insertions;
-
-  /* SSA names that had their defs inserted by PRE if do_pre.  */
-  bitmap inserted_exprs;
-
-  /* Blocks with statements that have had their EH properties changed.  */
-  bitmap need_eh_cleanup;
-
-  /* Blocks with statements that have had their AB properties changed.  */
-  bitmap need_ab_cleanup;
-
-  auto_vec<gimple *> to_remove;
-  auto_vec<gimple *> to_fixup;
-  auto_vec<tree> avail;
-  auto_vec<tree> avail_stack;
-};
-
-eliminate_dom_walker::eliminate_dom_walker (cdi_direction direction,
-                                           bitmap inserted_exprs_)
-  : dom_walker (direction), do_pre (inserted_exprs_ != NULL),
-    el_todo (0), eliminations (0), insertions (0),
-    inserted_exprs (inserted_exprs_)
-{
-  need_eh_cleanup = BITMAP_ALLOC (NULL);
-  need_ab_cleanup = BITMAP_ALLOC (NULL);
-}
-
-eliminate_dom_walker::~eliminate_dom_walker ()
+static tree
+vn_lookup_simplify_result (gimple_match_op *res_op)
 {
-  BITMAP_FREE (need_eh_cleanup);
-  BITMAP_FREE (need_ab_cleanup);
+  if (!res_op->code.is_tree_code ())
+    return NULL_TREE;
+  tree *ops = res_op->ops;
+  unsigned int length = res_op->num_ops;
+  if (res_op->code == CONSTRUCTOR
+      /* ???  We're arriving here with SCCVNs view, decomposed CONSTRUCTOR
+         and GIMPLEs / match-and-simplifies, CONSTRUCTOR as GENERIC tree.  */
+      && TREE_CODE (res_op->ops[0]) == CONSTRUCTOR)
+    {
+      length = CONSTRUCTOR_NELTS (res_op->ops[0]);
+      ops = XALLOCAVEC (tree, length);
+      for (unsigned i = 0; i < length; ++i)
+       ops[i] = CONSTRUCTOR_ELT (res_op->ops[0], i)->value;
+    }
+  vn_nary_op_t vnresult = NULL;
+  tree res = vn_nary_op_lookup_pieces (length, (tree_code) res_op->code,
+                                      res_op->type, ops, &vnresult);
+  /* If this is used from expression simplification make sure to
+     return an available expression.  */
+  if (res && TREE_CODE (res) == SSA_NAME && mprts_hook && rpo_avail)
+    res = rpo_avail->eliminate_avail (vn_context_bb, res);
+  return res;
 }
 
-/* Return a leader for OP that is available at the current point of the
-   eliminate domwalk.  */
+/* Return a leader for OPs value that is valid at BB.  */
 
 tree
-eliminate_dom_walker::eliminate_avail (tree op)
+rpo_elim::eliminate_avail (basic_block bb, tree op)
 {
-  tree valnum = VN_INFO (op)->valnum;
+  bool visited;
+  tree valnum = SSA_VAL (op, &visited);
+  /* If we didn't visit OP then it must be defined outside of the
+     region we process and also dominate it.  So it is available.  */
+  if (!visited)
+    return op;
   if (TREE_CODE (valnum) == SSA_NAME)
     {
       if (SSA_NAME_IS_DEFAULT_DEF (valnum))
        return valnum;
-      if (avail.length () > SSA_NAME_VERSION (valnum))
-       return avail[SSA_NAME_VERSION (valnum)];
+      vn_avail *av = VN_INFO (valnum)->avail;
+      if (!av)
+       return NULL_TREE;
+      if (av->location == bb->index)
+       /* On tramp3d 90% of the cases are here.  */
+       return ssa_name (av->leader);
+      do
+       {
+         basic_block abb = BASIC_BLOCK_FOR_FN (cfun, av->location);
+         /* ???  During elimination we have to use availability at the
+            definition site of a use we try to replace.  This
+            is required to not run into inconsistencies because
+            of dominated_by_p_w_unex behavior and removing a definition
+            while not replacing all uses.
+            ???  We could try to consistently walk dominators
+            ignoring non-executable regions.  The nearest common
+            dominator of bb and abb is where we can stop walking.  We
+            may also be able to "pre-compute" (bits of) the next immediate
+            (non-)dominator during the RPO walk when marking edges as
+            executable.  */
+         if (dominated_by_p_w_unex (bb, abb))
+           {
+             tree leader = ssa_name (av->leader);
+             /* Prevent eliminations that break loop-closed SSA.  */
+             if (loops_state_satisfies_p (LOOP_CLOSED_SSA)
+                 && ! SSA_NAME_IS_DEFAULT_DEF (leader)
+                 && ! flow_bb_inside_loop_p (gimple_bb (SSA_NAME_DEF_STMT
+                                                        (leader))->loop_father,
+                                             bb))
+               return NULL_TREE;
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 print_generic_expr (dump_file, leader);
+                 fprintf (dump_file, " is available for ");
+                 print_generic_expr (dump_file, valnum);
+                 fprintf (dump_file, "\n");
+               }
+             /* On tramp3d 99% of the _remaining_ cases succeed at
+                the first enty.  */
+             return leader;
+           }
+         /* ???  Can we somehow skip to the immediate dominator
+            RPO index (bb_to_rpo)?  Again, maybe not worth, on
+            tramp3d the worst number of elements in the vector is 9.  */
+         av = av->next;
+       }
+      while (av);
     }
-  else if (is_gimple_min_invariant (valnum))
+  else if (valnum != VN_TOP)
+    /* valnum is is_gimple_min_invariant.  */
     return valnum;
   return NULL_TREE;
 }
 
-/* At the current point of the eliminate domwalk make OP available.  */
+/* Make LEADER a leader for its value at BB.  */
 
 void
-eliminate_dom_walker::eliminate_push_avail (tree op)
+rpo_elim::eliminate_push_avail (basic_block bb, tree leader)
 {
-  tree valnum = VN_INFO (op)->valnum;
-  if (TREE_CODE (valnum) == SSA_NAME)
+  tree valnum = VN_INFO (leader)->valnum;
+  if (valnum == VN_TOP
+      || is_gimple_min_invariant (valnum))
+    return;
+  if (dump_file && (dump_flags & TDF_DETAILS))
     {
-      if (avail.length () <= SSA_NAME_VERSION (valnum))
-       avail.safe_grow_cleared (SSA_NAME_VERSION (valnum) + 1);
-      tree pushop = op;
-      if (avail[SSA_NAME_VERSION (valnum)])
-       pushop = avail[SSA_NAME_VERSION (valnum)];
-      avail_stack.safe_push (pushop);
-      avail[SSA_NAME_VERSION (valnum)] = op;
+      fprintf (dump_file, "Making available beyond BB%d ", bb->index);
+      print_generic_expr (dump_file, leader);
+      fprintf (dump_file, " for value ");
+      print_generic_expr (dump_file, valnum);
+      fprintf (dump_file, "\n");
+    }
+  vn_ssa_aux_t value = VN_INFO (valnum);
+  vn_avail *av;
+  if (m_avail_freelist)
+    {
+      av = m_avail_freelist;
+      m_avail_freelist = m_avail_freelist->next;
     }
+  else
+    av = XOBNEW (&vn_ssa_aux_obstack, vn_avail);
+  av->location = bb->index;
+  av->leader = SSA_NAME_VERSION (leader);
+  av->next = value->avail;
+  value->avail = av;
 }
 
-/* Insert the expression recorded by SCCVN for VAL at *GSI.  Returns
-   the leader for the expression if insertion was successful.  */
+/* Valueization hook for RPO VN plus required state.  */
 
 tree
-eliminate_dom_walker::eliminate_insert (gimple_stmt_iterator *gsi, tree val)
+rpo_vn_valueize (tree name)
 {
-  /* We can insert a sequence with a single assignment only.  */
-  gimple_seq stmts = VN_INFO (val)->expr;
-  if (!gimple_seq_singleton_p (stmts))
-    return NULL_TREE;
-  gassign *stmt = dyn_cast <gassign *> (gimple_seq_first_stmt (stmts));
-  if (!stmt
-      || (!CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
-         && gimple_assign_rhs_code (stmt) != VIEW_CONVERT_EXPR
-         && gimple_assign_rhs_code (stmt) != BIT_FIELD_REF
-         && (gimple_assign_rhs_code (stmt) != BIT_AND_EXPR
-             || TREE_CODE (gimple_assign_rhs2 (stmt)) != INTEGER_CST)))
-    return NULL_TREE;
-
-  tree op = gimple_assign_rhs1 (stmt);
-  if (gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
-      || gimple_assign_rhs_code (stmt) == BIT_FIELD_REF)
-    op = TREE_OPERAND (op, 0);
-  tree leader = TREE_CODE (op) == SSA_NAME ? eliminate_avail (op) : op;
-  if (!leader)
-    return NULL_TREE;
-
-  tree res;
-  stmts = NULL;
-  if (gimple_assign_rhs_code (stmt) == BIT_FIELD_REF)
-    res = gimple_build (&stmts, BIT_FIELD_REF,
-                       TREE_TYPE (val), leader,
-                       TREE_OPERAND (gimple_assign_rhs1 (stmt), 1),
-                       TREE_OPERAND (gimple_assign_rhs1 (stmt), 2));
-  else if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR)
-    res = gimple_build (&stmts, BIT_AND_EXPR,
-                       TREE_TYPE (val), leader, gimple_assign_rhs2 (stmt));
-  else
-    res = gimple_build (&stmts, gimple_assign_rhs_code (stmt),
-                       TREE_TYPE (val), leader);
-  if (TREE_CODE (res) != SSA_NAME
-      || SSA_NAME_IS_DEFAULT_DEF (res)
-      || gimple_bb (SSA_NAME_DEF_STMT (res)))
+  if (TREE_CODE (name) == SSA_NAME)
     {
-      gimple_seq_discard (stmts);
-
-      /* During propagation we have to treat SSA info conservatively
-         and thus we can end up simplifying the inserted expression
-        at elimination time to sth not defined in stmts.  */
-      /* But then this is a redundancy we failed to detect.  Which means
-         res now has two values.  That doesn't play well with how
-        we track availability here, so give up.  */
-      if (dump_file && (dump_flags & TDF_DETAILS))
+      vn_ssa_aux_t val = VN_INFO (name);
+      if (val)
        {
-         if (TREE_CODE (res) == SSA_NAME)
-           res = eliminate_avail (res);
-         if (res)
+         tree tem = val->valnum;
+         if (tem != VN_TOP && tem != name)
            {
-             fprintf (dump_file, "Failed to insert expression for value ");
-             print_generic_expr (dump_file, val);
-             fprintf (dump_file, " which is really fully redundant to ");
-             print_generic_expr (dump_file, res);
-             fprintf (dump_file, "\n");
+             if (TREE_CODE (tem) != SSA_NAME)
+               return tem;
+             /* For all values we only valueize to an available leader
+                which means we can use SSA name info without restriction.  */
+             tem = rpo_avail->eliminate_avail (vn_context_bb, tem);
+             if (tem)
+               return tem;
            }
        }
-
-      return NULL_TREE;
-    }
-  else
-    {
-      gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
-      VN_INFO_GET (res)->valnum = val;
     }
+  return name;
+}
 
-  insertions++;
-  if (dump_file && (dump_flags & TDF_DETAILS))
+/* Insert on PRED_E predicates derived from CODE OPS being true besides the
+   inverted condition.  */
+
+static void
+insert_related_predicates_on_edge (enum tree_code code, tree *ops, edge pred_e)
+{
+  switch (code)
     {
-      fprintf (dump_file, "Inserted ");
-      print_gimple_stmt (dump_file, SSA_NAME_DEF_STMT (res), 0);
+    case LT_EXPR:
+      /* a < b -> a {!,<}= b */
+      vn_nary_op_insert_pieces_predicated (2, NE_EXPR, boolean_type_node,
+                                          ops, boolean_true_node, 0, pred_e);
+      vn_nary_op_insert_pieces_predicated (2, LE_EXPR, boolean_type_node,
+                                          ops, boolean_true_node, 0, pred_e);
+      /* a < b -> ! a {>,=} b */
+      vn_nary_op_insert_pieces_predicated (2, GT_EXPR, boolean_type_node,
+                                          ops, boolean_false_node, 0, pred_e);
+      vn_nary_op_insert_pieces_predicated (2, EQ_EXPR, boolean_type_node,
+                                          ops, boolean_false_node, 0, pred_e);
+      break;
+    case GT_EXPR:
+      /* a > b -> a {!,>}= b */
+      vn_nary_op_insert_pieces_predicated (2, NE_EXPR, boolean_type_node,
+                                          ops, boolean_true_node, 0, pred_e);
+      vn_nary_op_insert_pieces_predicated (2, GE_EXPR, boolean_type_node,
+                                          ops, boolean_true_node, 0, pred_e);
+      /* a > b -> ! a {<,=} b */
+      vn_nary_op_insert_pieces_predicated (2, LT_EXPR, boolean_type_node,
+                                          ops, boolean_false_node, 0, pred_e);
+      vn_nary_op_insert_pieces_predicated (2, EQ_EXPR, boolean_type_node,
+                                          ops, boolean_false_node, 0, pred_e);
+      break;
+    case EQ_EXPR:
+      /* a == b -> ! a {<,>} b */
+      vn_nary_op_insert_pieces_predicated (2, LT_EXPR, boolean_type_node,
+                                          ops, boolean_false_node, 0, pred_e);
+      vn_nary_op_insert_pieces_predicated (2, GT_EXPR, boolean_type_node,
+                                          ops, boolean_false_node, 0, pred_e);
+      break;
+    case LE_EXPR:
+    case GE_EXPR:
+    case NE_EXPR:
+      /* Nothing besides inverted condition.  */
+      break;
+    default:;
     }
-
-  return res;
 }
 
+/* Main stmt worker for RPO VN, process BB.  */
 
-
-/* Perform elimination for the basic-block B during the domwalk.  */
-
-edge
-eliminate_dom_walker::before_dom_children (basic_block b)
+static unsigned
+process_bb (rpo_elim &avail, basic_block bb,
+           bool bb_visited, bool iterate_phis, bool iterate, bool eliminate,
+           bool do_region, bitmap exit_bbs, bool skip_phis)
 {
-  /* Mark new bb.  */
-  avail_stack.safe_push (NULL_TREE);
-
-  /* Skip unreachable blocks marked unreachable during the SCCVN domwalk.  */
+  unsigned todo = 0;
   edge_iterator ei;
   edge e;
-  FOR_EACH_EDGE (e, ei, b->preds)
-    if (e->flags & EDGE_EXECUTABLE)
-      break;
-  if (! e)
-    return NULL;
-
-  for (gphi_iterator gsi = gsi_start_phis (b); !gsi_end_p (gsi);)
-    {
-      gphi *phi = gsi.phi ();
-      tree res = PHI_RESULT (phi);
-
-      if (virtual_operand_p (res))
-       {
-         gsi_next (&gsi);
-         continue;
-       }
 
-      tree sprime = eliminate_avail (res);
-      if (sprime
-         && sprime != res)
+  vn_context_bb = bb;
+
+  /* If we are in loop-closed SSA preserve this state.  This is
+     relevant when called on regions from outside of FRE/PRE.  */
+  bool lc_phi_nodes = false;
+  if (!skip_phis
+      && loops_state_satisfies_p (LOOP_CLOSED_SSA))
+    FOR_EACH_EDGE (e, ei, bb->preds)
+      if (e->src->loop_father != e->dest->loop_father
+         && flow_loop_nested_p (e->dest->loop_father,
+                                e->src->loop_father))
        {
-         if (dump_file && (dump_flags & TDF_DETAILS))
-           {
-             fprintf (dump_file, "Replaced redundant PHI node defining ");
-             print_generic_expr (dump_file, res);
-             fprintf (dump_file, " with ");
-             print_generic_expr (dump_file, sprime);
-             fprintf (dump_file, "\n");
-           }
-
-         /* If we inserted this PHI node ourself, it's not an elimination.  */
-         if (! inserted_exprs
-             || ! bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res)))
-           eliminations++;
-
-         /* If we will propagate into all uses don't bother to do
-            anything.  */
-         if (may_propagate_copy (res, sprime))
-           {
-             /* Mark the PHI for removal.  */
-             to_remove.safe_push (phi);
-             gsi_next (&gsi);
-             continue;
-           }
-
-         remove_phi_node (&gsi, false);
-
-         if (!useless_type_conversion_p (TREE_TYPE (res), TREE_TYPE (sprime)))
-           sprime = fold_convert (TREE_TYPE (res), sprime);
-         gimple *stmt = gimple_build_assign (res, sprime);
-         gimple_stmt_iterator gsi2 = gsi_after_labels (b);
-         gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT);
-         continue;
+         lc_phi_nodes = true;
+         break;
        }
 
-      eliminate_push_avail (res);
-      gsi_next (&gsi);
-    }
-
-  for (gimple_stmt_iterator gsi = gsi_start_bb (b);
-       !gsi_end_p (gsi);
-       gsi_next (&gsi))
+  /* When we visit a loop header substitute into loop info.  */
+  if (!iterate && eliminate && bb->loop_father->header == bb)
     {
-      tree sprime = NULL_TREE;
-      gimple *stmt = gsi_stmt (gsi);
-      tree lhs = gimple_get_lhs (stmt);
-      if (lhs && TREE_CODE (lhs) == SSA_NAME
-         && !gimple_has_volatile_ops (stmt)
-         /* See PR43491.  Do not replace a global register variable when
-            it is a the RHS of an assignment.  Do replace local register
-            variables since gcc does not guarantee a local variable will
-            be allocated in register.
-            ???  The fix isn't effective here.  This should instead
-            be ensured by not value-numbering them the same but treating
-            them like volatiles?  */
-         && !(gimple_assign_single_p (stmt)
-              && (TREE_CODE (gimple_assign_rhs1 (stmt)) == VAR_DECL
-                  && DECL_HARD_REGISTER (gimple_assign_rhs1 (stmt))
-                  && is_global_var (gimple_assign_rhs1 (stmt)))))
-       {
-         sprime = eliminate_avail (lhs);
-         if (!sprime)
-           {
-             /* If there is no existing usable leader but SCCVN thinks
-                it has an expression it wants to use as replacement,
-                insert that.  */
-             tree val = VN_INFO (lhs)->valnum;
-             if (val != VN_TOP
-                 && TREE_CODE (val) == SSA_NAME
-                 && VN_INFO (val)->needs_insertion
-                 && VN_INFO (val)->expr != NULL
-                 && (sprime = eliminate_insert (&gsi, val)) != NULL_TREE)
-               eliminate_push_avail (sprime);
-           }
-
-         /* If this now constitutes a copy duplicate points-to
-            and range info appropriately.  This is especially
-            important for inserted code.  See tree-ssa-copy.c
-            for similar code.  */
-         if (sprime
-             && TREE_CODE (sprime) == SSA_NAME)
-           {
-             basic_block sprime_b = gimple_bb (SSA_NAME_DEF_STMT (sprime));
-             if (POINTER_TYPE_P (TREE_TYPE (lhs))
-                 && VN_INFO_PTR_INFO (lhs)
-                 && ! VN_INFO_PTR_INFO (sprime))
-               {
-                 duplicate_ssa_name_ptr_info (sprime,
-                                              VN_INFO_PTR_INFO (lhs));
-                 if (b != sprime_b)
-                   mark_ptr_info_alignment_unknown
-                       (SSA_NAME_PTR_INFO (sprime));
-               }
-             else if (INTEGRAL_TYPE_P (TREE_TYPE (lhs))
-                      && VN_INFO_RANGE_INFO (lhs)
-                      && ! VN_INFO_RANGE_INFO (sprime)
-                      && b == sprime_b)
-               duplicate_ssa_name_range_info (sprime,
-                                              VN_INFO_RANGE_TYPE (lhs),
-                                              VN_INFO_RANGE_INFO (lhs));
-           }
-
-         /* Inhibit the use of an inserted PHI on a loop header when
-            the address of the memory reference is a simple induction
-            variable.  In other cases the vectorizer won't do anything
-            anyway (either it's loop invariant or a complicated
-            expression).  */
-         if (sprime
-             && TREE_CODE (sprime) == SSA_NAME
-             && do_pre
-             && (flag_tree_loop_vectorize || flag_tree_parallelize_loops > 1)
-             && loop_outer (b->loop_father)
-             && has_zero_uses (sprime)
-             && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime))
-             && gimple_assign_load_p (stmt))
-           {
-             gimple *def_stmt = SSA_NAME_DEF_STMT (sprime);
-             basic_block def_bb = gimple_bb (def_stmt);
-             if (gimple_code (def_stmt) == GIMPLE_PHI
-                 && def_bb->loop_father->header == def_bb)
-               {
-                 loop_p loop = def_bb->loop_father;
-                 ssa_op_iter iter;
-                 tree op;
-                 bool found = false;
-                 FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
-                   {
-                     affine_iv iv;
-                     def_bb = gimple_bb (SSA_NAME_DEF_STMT (op));
-                     if (def_bb
-                         && flow_bb_inside_loop_p (loop, def_bb)
-                         && simple_iv (loop, loop, op, &iv, true))
-                       {
-                         found = true;
-                         break;
-                       }
-                   }
-                 if (found)
-                   {
-                     if (dump_file && (dump_flags & TDF_DETAILS))
-                       {
-                         fprintf (dump_file, "Not replacing ");
-                         print_gimple_expr (dump_file, stmt, 0);
-                         fprintf (dump_file, " with ");
-                         print_generic_expr (dump_file, sprime);
-                         fprintf (dump_file, " which would add a loop"
-                                  " carried dependence to loop %d\n",
-                                  loop->num);
-                       }
-                     /* Don't keep sprime available.  */
-                     sprime = NULL_TREE;
-                   }
-               }
-           }
-
-         if (sprime)
-           {
-             /* If we can propagate the value computed for LHS into
-                all uses don't bother doing anything with this stmt.  */
-             if (may_propagate_copy (lhs, sprime))
-               {
-                 /* Mark it for removal.  */
-                 to_remove.safe_push (stmt);
-
-                 /* ???  Don't count copy/constant propagations.  */
-                 if (gimple_assign_single_p (stmt)
-                     && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
-                         || gimple_assign_rhs1 (stmt) == sprime))
-                   continue;
-
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   {
-                     fprintf (dump_file, "Replaced ");
-                     print_gimple_expr (dump_file, stmt, 0);
-                     fprintf (dump_file, " with ");
-                     print_generic_expr (dump_file, sprime);
-                     fprintf (dump_file, " in all uses of ");
-                     print_gimple_stmt (dump_file, stmt, 0);
-                   }
-
-                 eliminations++;
-                 continue;
-               }
-
-             /* If this is an assignment from our leader (which
-                happens in the case the value-number is a constant)
-                then there is nothing to do.  */
-             if (gimple_assign_single_p (stmt)
-                 && sprime == gimple_assign_rhs1 (stmt))
-               continue;
-
-             /* Else replace its RHS.  */
-             bool can_make_abnormal_goto
-                 = is_gimple_call (stmt)
-                 && stmt_can_make_abnormal_goto (stmt);
-
-             if (dump_file && (dump_flags & TDF_DETAILS))
-               {
-                 fprintf (dump_file, "Replaced ");
-                 print_gimple_expr (dump_file, stmt, 0);
-                 fprintf (dump_file, " with ");
-                 print_generic_expr (dump_file, sprime);
-                 fprintf (dump_file, " in ");
-                 print_gimple_stmt (dump_file, stmt, 0);
-               }
+      /* Keep fields in sync with substitute_in_loop_info.  */
+      if (bb->loop_father->nb_iterations)
+       bb->loop_father->nb_iterations
+         = simplify_replace_tree (bb->loop_father->nb_iterations,
+                                  NULL_TREE, NULL_TREE, vn_valueize);
+    }
 
-             eliminations++;
-             gimple *orig_stmt = stmt;
-             if (!useless_type_conversion_p (TREE_TYPE (lhs),
-                                             TREE_TYPE (sprime)))
-               sprime = fold_convert (TREE_TYPE (lhs), sprime);
-             tree vdef = gimple_vdef (stmt);
-             tree vuse = gimple_vuse (stmt);
-             propagate_tree_value_into_stmt (&gsi, sprime);
-             stmt = gsi_stmt (gsi);
-             update_stmt (stmt);
-             if (vdef != gimple_vdef (stmt))
-               VN_INFO (vdef)->valnum = vuse;
-
-             /* If we removed EH side-effects from the statement, clean
-                its EH information.  */
-             if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt))
-               {
-                 bitmap_set_bit (need_eh_cleanup,
-                                 gimple_bb (stmt)->index);
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   fprintf (dump_file, "  Removed EH side-effects.\n");
-               }
+  /* Value-number all defs in the basic-block.  */
+  if (!skip_phis)
+    for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+        gsi_next (&gsi))
+      {
+       gphi *phi = gsi.phi ();
+       tree res = PHI_RESULT (phi);
+       vn_ssa_aux_t res_info = VN_INFO (res);
+       if (!bb_visited)
+         {
+           gcc_assert (!res_info->visited);
+           res_info->valnum = VN_TOP;
+           res_info->visited = true;
+         }
 
-             /* Likewise for AB side-effects.  */
-             if (can_make_abnormal_goto
-                 && !stmt_can_make_abnormal_goto (stmt))
-               {
-                 bitmap_set_bit (need_ab_cleanup,
-                                 gimple_bb (stmt)->index);
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   fprintf (dump_file, "  Removed AB side-effects.\n");
-               }
+       /* When not iterating force backedge values to varying.  */
+       visit_stmt (phi, !iterate_phis);
+       if (virtual_operand_p (res))
+         continue;
 
-             continue;
-           }
-       }
+       /* Eliminate */
+       /* The interesting case is gcc.dg/tree-ssa/pr22230.c for correctness
+          how we handle backedges and availability.
+          And gcc.dg/tree-ssa/ssa-sccvn-2.c for optimization.  */
+       tree val = res_info->valnum;
+       if (res != val && !iterate && eliminate)
+         {
+           if (tree leader = avail.eliminate_avail (bb, res))
+             {
+               if (leader != res
+                   /* Preserve loop-closed SSA form.  */
+                   && (! lc_phi_nodes
+                       || is_gimple_min_invariant (leader)))
+                 {
+                   if (dump_file && (dump_flags & TDF_DETAILS))
+                     {
+                       fprintf (dump_file, "Replaced redundant PHI node "
+                                "defining ");
+                       print_generic_expr (dump_file, res);
+                       fprintf (dump_file, " with ");
+                       print_generic_expr (dump_file, leader);
+                       fprintf (dump_file, "\n");
+                     }
+                   avail.eliminations++;
+
+                   if (may_propagate_copy (res, leader))
+                     {
+                       /* Schedule for removal.  */
+                       avail.to_remove.safe_push (phi);
+                       continue;
+                     }
+                   /* ???  Else generate a copy stmt.  */
+                 }
+             }
+         }
+       /* Only make defs available that not already are.  But make
+          sure loop-closed SSA PHI node defs are picked up for
+          downstream uses.  */
+       if (lc_phi_nodes
+           || res == val
+           || ! avail.eliminate_avail (bb, res))
+         avail.eliminate_push_avail (bb, res);
+      }
 
-      /* If the statement is a scalar store, see if the expression
-         has the same value number as its rhs.  If so, the store is
-         dead.  */
-      if (gimple_assign_single_p (stmt)
-         && !gimple_has_volatile_ops (stmt)
-         && !is_gimple_reg (gimple_assign_lhs (stmt))
-         && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
-             || is_gimple_min_invariant (gimple_assign_rhs1 (stmt))))
+  /* For empty BBs mark outgoing edges executable.  For non-empty BBs
+     we do this when processing the last stmt as we have to do this
+     before elimination which otherwise forces GIMPLE_CONDs to
+     if (1 != 0) style when seeing non-executable edges.  */
+  if (gsi_end_p (gsi_start_bb (bb)))
+    {
+      FOR_EACH_EDGE (e, ei, bb->succs)
        {
-         tree val;
-         tree rhs = gimple_assign_rhs1 (stmt);
-         vn_reference_t vnresult;
-         val = vn_reference_lookup (lhs, gimple_vuse (stmt), VN_WALKREWRITE,
-                                    &vnresult, false);
-         if (TREE_CODE (rhs) == SSA_NAME)
-           rhs = VN_INFO (rhs)->valnum;
-         if (val
-             && operand_equal_p (val, rhs, 0))
+         if (!(e->flags & EDGE_EXECUTABLE))
            {
-             /* We can only remove the later store if the former aliases
-                at least all accesses the later one does or if the store
-                was to readonly memory storing the same value.  */
-             alias_set_type set = get_alias_set (lhs);
-             if (! vnresult
-                 || vnresult->set == set
-                 || alias_set_subset_of (set, vnresult->set))
-               {
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   {
-                     fprintf (dump_file, "Deleted redundant store ");
-                     print_gimple_stmt (dump_file, stmt, 0);
-                   }
-
-                 /* Queue stmt for removal.  */
-                 to_remove.safe_push (stmt);
-                 continue;
-               }
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file,
+                        "marking outgoing edge %d -> %d executable\n",
+                        e->src->index, e->dest->index);
+             e->flags |= EDGE_EXECUTABLE;
+             e->dest->flags |= BB_EXECUTABLE;
            }
-       }
-
-      /* If this is a control statement value numbering left edges
-        unexecuted on force the condition in a way consistent with
-        that.  */
-      if (gcond *cond = dyn_cast <gcond *> (stmt))
-       {
-         if ((EDGE_SUCC (b, 0)->flags & EDGE_EXECUTABLE)
-             ^ (EDGE_SUCC (b, 1)->flags & EDGE_EXECUTABLE))
+         else if (!(e->dest->flags & BB_EXECUTABLE))
            {
-              if (dump_file && (dump_flags & TDF_DETAILS))
-                {
-                  fprintf (dump_file, "Removing unexecutable edge from ");
-                 print_gimple_stmt (dump_file, stmt, 0);
-                }
-             if (((EDGE_SUCC (b, 0)->flags & EDGE_TRUE_VALUE) != 0)
-                 == ((EDGE_SUCC (b, 0)->flags & EDGE_EXECUTABLE) != 0))
-               gimple_cond_make_true (cond);
-             else
-               gimple_cond_make_false (cond);
-             update_stmt (cond);
-             el_todo |= TODO_cleanup_cfg;
-             continue;
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file,
+                        "marking destination block %d reachable\n",
+                        e->dest->index);
+             e->dest->flags |= BB_EXECUTABLE;
            }
        }
-
-      bool can_make_abnormal_goto = stmt_can_make_abnormal_goto (stmt);
-      bool was_noreturn = (is_gimple_call (stmt)
-                          && gimple_call_noreturn_p (stmt));
-      tree vdef = gimple_vdef (stmt);
-      tree vuse = gimple_vuse (stmt);
-
-      /* If we didn't replace the whole stmt (or propagate the result
-         into all uses), replace all uses on this stmt with their
-        leaders.  */
-      bool modified = false;
-      use_operand_p use_p;
-      ssa_op_iter iter;
-      FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
+    }
+  for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+       !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      ssa_op_iter i;
+      tree op;
+      if (!bb_visited)
        {
-         tree use = USE_FROM_PTR (use_p);
-         /* ???  The call code above leaves stmt operands un-updated.  */
-         if (TREE_CODE (use) != SSA_NAME)
-           continue;
-         tree sprime = eliminate_avail (use);
-         if (sprime && sprime != use
-             && may_propagate_copy (use, sprime)
-             /* We substitute into debug stmts to avoid excessive
-                debug temporaries created by removed stmts, but we need
-                to avoid doing so for inserted sprimes as we never want
-                to create debug temporaries for them.  */
-             && (!inserted_exprs
-                 || TREE_CODE (sprime) != SSA_NAME
-                 || !is_gimple_debug (stmt)
-                 || !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime))))
+         FOR_EACH_SSA_TREE_OPERAND (op, gsi_stmt (gsi), i, SSA_OP_ALL_DEFS)
            {
-             propagate_value (use_p, sprime);
-             modified = true;
+             vn_ssa_aux_t op_info = VN_INFO (op);
+             gcc_assert (!op_info->visited);
+             op_info->valnum = VN_TOP;
+             op_info->visited = true;
            }
+
+         /* We somehow have to deal with uses that are not defined
+            in the processed region.  Forcing unvisited uses to
+            varying here doesn't play well with def-use following during
+            expression simplification, so we deal with this by checking
+            the visited flag in SSA_VAL.  */
        }
 
-      /* Fold the stmt if modified, this canonicalizes MEM_REFs we propagated
-         into which is a requirement for the IPA devirt machinery.  */
-      gimple *old_stmt = stmt;
-      if (modified)
+      visit_stmt (gsi_stmt (gsi));
+
+      gimple *last = gsi_stmt (gsi);
+      e = NULL;
+      switch (gimple_code (last))
        {
-         /* If a formerly non-invariant ADDR_EXPR is turned into an
-            invariant one it was on a separate stmt.  */
-         if (gimple_assign_single_p (stmt)
-             && TREE_CODE (gimple_assign_rhs1 (stmt)) == ADDR_EXPR)
-           recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt));
-         gimple_stmt_iterator prev = gsi;
-         gsi_prev (&prev);
-         if (fold_stmt (&gsi))
-           {
-             /* fold_stmt may have created new stmts inbetween
-                the previous stmt and the folded stmt.  Mark
-                all defs created there as varying to not confuse
-                the SCCVN machinery as we're using that even during
-                elimination.  */
-             if (gsi_end_p (prev))
-               prev = gsi_start_bb (b);
-             else
-               gsi_next (&prev);
-             if (gsi_stmt (prev) != gsi_stmt (gsi))
-               do
+       case GIMPLE_SWITCH:
+         e = find_taken_edge (bb, vn_valueize (gimple_switch_index
+                                               (as_a <gswitch *> (last))));
+         break;
+       case GIMPLE_COND:
+         {
+           tree lhs = vn_valueize (gimple_cond_lhs (last));
+           tree rhs = vn_valueize (gimple_cond_rhs (last));
+           tree val = gimple_simplify (gimple_cond_code (last),
+                                       boolean_type_node, lhs, rhs,
+                                       NULL, vn_valueize);
+           /* 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;
+               val = vn_nary_op_lookup_pieces (2, gimple_cond_code (last),
+                                               boolean_type_node, ops,
+                                               &vnresult);
+               /* Did we get a predicated value?  */
+               if (! val && vnresult && vnresult->predicated_values)
                  {
-                   tree def;
-                   ssa_op_iter dit;
-                   FOR_EACH_SSA_TREE_OPERAND (def, gsi_stmt (prev),
-                                              dit, SSA_OP_ALL_DEFS)
-                     /* As existing DEFs may move between stmts
-                        we have to guard VN_INFO_GET.  */
-                     if (! has_VN_INFO (def))
-                       VN_INFO_GET (def)->valnum = def;
-                   if (gsi_stmt (prev) == gsi_stmt (gsi))
-                     break;
-                   gsi_next (&prev);
+                   val = vn_nary_op_get_predicated_value (vnresult, bb);
+                   if (val && dump_file && (dump_flags & TDF_DETAILS))
+                     {
+                       fprintf (dump_file, "Got predicated value ");
+                       print_generic_expr (dump_file, val, TDF_NONE);
+                       fprintf (dump_file, " for ");
+                       print_gimple_stmt (dump_file, last, TDF_SLIM);
+                     }
                  }
-               while (1);
-           }
-         stmt = gsi_stmt (gsi);
-         /* In case we folded the stmt away schedule the NOP for removal.  */
-         if (gimple_nop_p (stmt))
-           to_remove.safe_push (stmt);
+             }
+           if (val)
+             e = find_taken_edge (bb, val);
+           if (! e)
+             {
+               /* If we didn't manage to compute the taken edge then
+                  push predicated expressions for the condition itself
+                  and related conditions to the hashtables.  This allows
+                  simplification of redundant conditions which is
+                  important as early cleanup.  */
+               edge true_e, false_e;
+               extract_true_false_edges_from_block (bb, &true_e, &false_e);
+               enum tree_code code = gimple_cond_code (last);
+               enum tree_code icode
+                 = invert_tree_comparison (code, HONOR_NANS (lhs));
+               tree ops[2];
+               ops[0] = lhs;
+               ops[1] = rhs;
+               if (do_region
+                   && bitmap_bit_p (exit_bbs, true_e->dest->index))
+                 true_e = NULL;
+               if (do_region
+                   && bitmap_bit_p (exit_bbs, false_e->dest->index))
+                 false_e = NULL;
+               if (true_e)
+                 vn_nary_op_insert_pieces_predicated
+                   (2, code, boolean_type_node, ops,
+                    boolean_true_node, 0, true_e);
+               if (false_e)
+                 vn_nary_op_insert_pieces_predicated
+                   (2, code, boolean_type_node, ops,
+                    boolean_false_node, 0, false_e);
+               if (icode != ERROR_MARK)
+                 {
+                   if (true_e)
+                     vn_nary_op_insert_pieces_predicated
+                       (2, icode, boolean_type_node, ops,
+                        boolean_false_node, 0, true_e);
+                   if (false_e)
+                     vn_nary_op_insert_pieces_predicated
+                       (2, icode, boolean_type_node, ops,
+                        boolean_true_node, 0, false_e);
+                 }
+               /* Relax for non-integers, inverted condition handled
+                  above.  */
+               if (INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+                 {
+                   if (true_e)
+                     insert_related_predicates_on_edge (code, ops, true_e);
+                   if (false_e)
+                     insert_related_predicates_on_edge (icode, ops, false_e);
+                 }
+             }
+           break;
+         }
+       case GIMPLE_GOTO:
+         e = find_taken_edge (bb, vn_valueize (gimple_goto_dest (last)));
+         break;
+       default:
+         e = NULL;
        }
-
-      /* Visit indirect calls and turn them into direct calls if
-        possible using the devirtualization machinery.  Do this before
-        checking for required EH/abnormal/noreturn cleanup as devird
-        may expose more of those.  */
-      if (gcall *call_stmt = dyn_cast <gcall *> (stmt))
+      if (e)
        {
-         tree fn = gimple_call_fn (call_stmt);
-         if (fn
-             && flag_devirtualize
-             && virtual_method_call_p (fn))
+         todo = TODO_cleanup_cfg;
+         if (!(e->flags & EDGE_EXECUTABLE))
            {
-             tree otr_type = obj_type_ref_class (fn);
-             unsigned HOST_WIDE_INT otr_tok
-               = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (fn));
-             tree instance;
-             ipa_polymorphic_call_context context (current_function_decl,
-                                                   fn, stmt, &instance);
-             context.get_dynamic_type (instance, OBJ_TYPE_REF_OBJECT (fn),
-                                       otr_type, stmt);
-             bool final;
-             vec <cgraph_node *> targets
-               = possible_polymorphic_call_targets (obj_type_ref_class (fn),
-                                                    otr_tok, context, &final);
-             if (dump_file)
-               dump_possible_polymorphic_call_targets (dump_file, 
-                                                       obj_type_ref_class (fn),
-                                                       otr_tok, context);
-             if (final && targets.length () <= 1 && dbg_cnt (devirt))
-               {
-                 tree fn;
-                 if (targets.length () == 1)
-                   fn = targets[0]->decl;
-                 else
-                   fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
-                 if (dump_enabled_p ())
-                   {
-                     dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-                                      "converting indirect call to "
-                                      "function %s\n",
-                                      lang_hooks.decl_printable_name (fn, 2));
-                   }
-                 gimple_call_set_fndecl (call_stmt, fn);
-                 /* If changing the call to __builtin_unreachable
-                    or similar noreturn function, adjust gimple_call_fntype
-                    too.  */
-                 if (gimple_call_noreturn_p (call_stmt)
-                     && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fn)))
-                     && TYPE_ARG_TYPES (TREE_TYPE (fn))
-                     && (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn)))
-                         == void_type_node))
-                   gimple_call_set_fntype (call_stmt, TREE_TYPE (fn));
-                 maybe_remove_unused_call_args (cfun, call_stmt);
-                 modified = true;
-               }
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file,
+                        "marking known outgoing %sedge %d -> %d executable\n",
+                        e->flags & EDGE_DFS_BACK ? "back-" : "",
+                        e->src->index, e->dest->index);
+             e->flags |= EDGE_EXECUTABLE;
+             e->dest->flags |= BB_EXECUTABLE;
            }
-       }
-
-      if (modified)
-       {
-         /* When changing a call into a noreturn call, cfg cleanup
-            is needed to fix up the noreturn call.  */
-         if (!was_noreturn
-             && is_gimple_call (stmt) && gimple_call_noreturn_p (stmt))
-           to_fixup.safe_push  (stmt);
-         /* When changing a condition or switch into one we know what
-            edge will be executed, schedule a cfg cleanup.  */
-         if ((gimple_code (stmt) == GIMPLE_COND
-              && (gimple_cond_true_p (as_a <gcond *> (stmt))
-                  || gimple_cond_false_p (as_a <gcond *> (stmt))))
-             || (gimple_code (stmt) == GIMPLE_SWITCH
-                 && TREE_CODE (gimple_switch_index
-                                 (as_a <gswitch *> (stmt))) == INTEGER_CST))
-           el_todo |= TODO_cleanup_cfg;
-         /* If we removed EH side-effects from the statement, clean
-            its EH information.  */
-         if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
+         else if (!(e->dest->flags & BB_EXECUTABLE))
            {
-             bitmap_set_bit (need_eh_cleanup,
-                             gimple_bb (stmt)->index);
              if (dump_file && (dump_flags & TDF_DETAILS))
-               fprintf (dump_file, "  Removed EH side-effects.\n");
+               fprintf (dump_file,
+                        "marking destination block %d reachable\n",
+                        e->dest->index);
+             e->dest->flags |= BB_EXECUTABLE;
            }
-         /* Likewise for AB side-effects.  */
-         if (can_make_abnormal_goto
-             && !stmt_can_make_abnormal_goto (stmt))
+       }
+      else if (gsi_one_before_end_p (gsi))
+       {
+         FOR_EACH_EDGE (e, ei, bb->succs)
            {
-             bitmap_set_bit (need_ab_cleanup,
-                             gimple_bb (stmt)->index);
-             if (dump_file && (dump_flags & TDF_DETAILS))
-               fprintf (dump_file, "  Removed AB side-effects.\n");
+             if (!(e->flags & EDGE_EXECUTABLE))
+               {
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   fprintf (dump_file,
+                            "marking outgoing edge %d -> %d executable\n",
+                            e->src->index, e->dest->index);
+                 e->flags |= EDGE_EXECUTABLE;
+                 e->dest->flags |= BB_EXECUTABLE;
+               }
+             else if (!(e->dest->flags & BB_EXECUTABLE))
+               {
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   fprintf (dump_file,
+                            "marking destination block %d reachable\n",
+                            e->dest->index);
+                 e->dest->flags |= BB_EXECUTABLE;
+               }
            }
-         update_stmt (stmt);
-         if (vdef != gimple_vdef (stmt))
-           VN_INFO (vdef)->valnum = vuse;
        }
 
-      /* Make new values available - for fully redundant LHS we
-         continue with the next stmt above and skip this.  */
-      def_operand_p defp;
-      FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF)
-       eliminate_push_avail (DEF_FROM_PTR (defp));
+      /* Eliminate.  That also pushes to avail.  */
+      if (eliminate && ! iterate)
+       avail.eliminate_stmt (bb, &gsi);
+      else
+       /* If not eliminating, make all not already available defs
+          available.  */
+       FOR_EACH_SSA_TREE_OPERAND (op, gsi_stmt (gsi), i, SSA_OP_DEF)
+         if (! avail.eliminate_avail (bb, op))
+           avail.eliminate_push_avail (bb, op);
     }
 
-  /* Replace destination PHI arguments.  */
-  FOR_EACH_EDGE (e, ei, b->succs)
-    if (e->flags & EDGE_EXECUTABLE)
+  /* Eliminate in destination PHI arguments.  Always substitute in dest
+     PHIs, even for non-executable edges.  This handles region
+     exits PHIs.  */
+  if (!iterate && eliminate)
+    FOR_EACH_EDGE (e, ei, bb->succs)
       for (gphi_iterator gsi = gsi_start_phis (e->dest);
-          !gsi_end_p (gsi);
-          gsi_next (&gsi))
+          !gsi_end_p (gsi); gsi_next (&gsi))
        {
          gphi *phi = gsi.phi ();
          use_operand_p use_p = PHI_ARG_DEF_PTR_FROM_EDGE (phi, e);
@@ -5935,106 +6653,567 @@ eliminate_dom_walker::before_dom_children (basic_block b)
          if (TREE_CODE (arg) != SSA_NAME
              || virtual_operand_p (arg))
            continue;
-         tree sprime = eliminate_avail (arg);
-         if (sprime && may_propagate_copy (arg, sprime))
+         tree sprime;
+         if (SSA_NAME_IS_DEFAULT_DEF (arg))
+           {
+             sprime = SSA_VAL (arg);
+             gcc_assert (TREE_CODE (sprime) != SSA_NAME
+                         || SSA_NAME_IS_DEFAULT_DEF (sprime));
+           }
+         else
+           /* Look for sth available at the definition block of the argument.
+              This avoids inconsistencies between availability there which
+              decides if the stmt can be removed and availability at the
+              use site.  The SSA property ensures that things available
+              at the definition are also available at uses.  */
+           sprime = avail.eliminate_avail (gimple_bb (SSA_NAME_DEF_STMT (arg)),
+                                           arg);
+         if (sprime
+             && sprime != arg
+             && may_propagate_copy (arg, sprime))
            propagate_value (use_p, sprime);
        }
-  return NULL;
+
+  vn_context_bb = NULL;
+  return todo;
 }
 
-/* Make no longer available leaders no longer available.  */
+/* Unwind state per basic-block.  */
 
-void
-eliminate_dom_walker::after_dom_children (basic_block)
+struct unwind_state
 {
-  tree entry;
-  while ((entry = avail_stack.pop ()) != NULL_TREE)
+  /* Times this block has been visited.  */
+  unsigned visited;
+  /* Whether to handle this as iteration point or whether to treat
+     incoming backedge PHI values as varying.  */
+  bool iterate;
+  /* Maximum RPO index this block is reachable from.  */
+  int max_rpo;
+  /* Unwind state.  */
+  void *ob_top;
+  vn_reference_t ref_top;
+  vn_phi_t phi_top;
+  vn_nary_op_t nary_top;
+};
+
+/* Unwind the RPO VN state for iteration.  */
+
+static void
+do_unwind (unwind_state *to, int rpo_idx, rpo_elim &avail, int *bb_to_rpo)
+{
+  gcc_assert (to->iterate);
+  for (; last_inserted_nary != to->nary_top;
+       last_inserted_nary = last_inserted_nary->next)
     {
-      tree valnum = VN_INFO (entry)->valnum;
-      tree old = avail[SSA_NAME_VERSION (valnum)];
-      if (old == entry)
-       avail[SSA_NAME_VERSION (valnum)] = NULL_TREE;
+      vn_nary_op_t *slot;
+      slot = valid_info->nary->find_slot_with_hash
+       (last_inserted_nary, last_inserted_nary->hashcode, NO_INSERT);
+      /* Predication causes the need to restore previous state.  */
+      if ((*slot)->unwind_to)
+       *slot = (*slot)->unwind_to;
       else
-       avail[SSA_NAME_VERSION (valnum)] = entry;
+       valid_info->nary->clear_slot (slot);
+    }
+  for (; last_inserted_phi != to->phi_top;
+       last_inserted_phi = last_inserted_phi->next)
+    {
+      vn_phi_t *slot;
+      slot = valid_info->phis->find_slot_with_hash
+       (last_inserted_phi, last_inserted_phi->hashcode, NO_INSERT);
+      valid_info->phis->clear_slot (slot);
+    }
+  for (; last_inserted_ref != to->ref_top;
+       last_inserted_ref = last_inserted_ref->next)
+    {
+      vn_reference_t *slot;
+      slot = valid_info->references->find_slot_with_hash
+       (last_inserted_ref, last_inserted_ref->hashcode, NO_INSERT);
+      (*slot)->operands.release ();
+      valid_info->references->clear_slot (slot);
+    }
+  obstack_free (&vn_tables_obstack, to->ob_top);
+
+  /* Prune [rpo_idx, ] from avail.  */
+  /* ???  This is O(number-of-values-in-region) which is
+     O(region-size) rather than O(iteration-piece).  */
+  for (hash_table<vn_ssa_aux_hasher>::iterator i = vn_ssa_aux_hash->begin ();
+       i != vn_ssa_aux_hash->end (); ++i)
+    {
+      while ((*i)->avail)
+       {
+         if (bb_to_rpo[(*i)->avail->location] < rpo_idx)
+           break;
+         vn_avail *av = (*i)->avail;
+         (*i)->avail = (*i)->avail->next;
+         av->next = avail.m_avail_freelist;
+         avail.m_avail_freelist = av;
+       }
     }
 }
 
-/* Eliminate fully redundant computations.  */
+/* Do VN on a SEME region specified by ENTRY and EXIT_BBS in FN.
+   If ITERATE is true then treat backedges optimistically as not
+   executed and iterate.  If ELIMINATE is true then perform
+   elimination, otherwise leave that to the caller.  */
 
-unsigned int
-vn_eliminate (bitmap inserted_exprs)
+static unsigned
+do_rpo_vn (function *fn, edge entry, bitmap exit_bbs,
+          bool iterate, bool eliminate)
 {
-  eliminate_dom_walker el (CDI_DOMINATORS, inserted_exprs);
-  el.avail.reserve (num_ssa_names);
+  unsigned todo = 0;
 
-  el.walk (cfun->cfg->x_entry_block_ptr);
+  /* We currently do not support region-based iteration when
+     elimination is requested.  */
+  gcc_assert (!entry || !iterate || !eliminate);
+  /* When iterating we need loop info up-to-date.  */
+  gcc_assert (!iterate || !loops_state_satisfies_p (LOOPS_NEED_FIXUP));
 
-  /* We cannot remove stmts during BB walk, especially not release SSA
-     names there as this confuses the VN machinery.  The stmts ending
-     up in to_remove are either stores or simple copies.
-     Remove stmts in reverse order to make debug stmt creation possible.  */
-  while (!el.to_remove.is_empty ())
+  bool do_region = entry != NULL;
+  if (!do_region)
     {
-      gimple *stmt = el.to_remove.pop ();
+      entry = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fn));
+      exit_bbs = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (exit_bbs, EXIT_BLOCK);
+    }
 
-      if (dump_file && (dump_flags & TDF_DETAILS))
+  /* Clear EDGE_DFS_BACK on "all" entry edges, RPO order compute will
+     re-mark those that are contained in the region.  */
+  edge_iterator ei;
+  edge e;
+  FOR_EACH_EDGE (e, ei, entry->dest->preds)
+    e->flags &= ~EDGE_DFS_BACK;
+
+  int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fn) - NUM_FIXED_BLOCKS);
+  int n = rev_post_order_and_mark_dfs_back_seme
+    (fn, entry, exit_bbs, !loops_state_satisfies_p (LOOPS_NEED_FIXUP), rpo);
+  /* rev_post_order_and_mark_dfs_back_seme fills RPO in reverse order.  */
+  for (int i = 0; i < n / 2; ++i)
+    std::swap (rpo[i], rpo[n-i-1]);
+
+  if (!do_region)
+    BITMAP_FREE (exit_bbs);
+
+  /* If there are any non-DFS_BACK edges into entry->dest skip
+     processing PHI nodes for that block.  This supports
+     value-numbering loop bodies w/o the actual loop.  */
+  FOR_EACH_EDGE (e, ei, entry->dest->preds)
+    if (e != entry
+       && !(e->flags & EDGE_DFS_BACK))
+      break;
+  bool skip_entry_phis = e != NULL;
+  if (skip_entry_phis && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Region does not contain all edges into "
+            "the entry block, skipping its PHIs.\n");
+
+  int *bb_to_rpo = XNEWVEC (int, last_basic_block_for_fn (fn));
+  for (int i = 0; i < n; ++i)
+    bb_to_rpo[rpo[i]] = i;
+
+  unwind_state *rpo_state = XNEWVEC (unwind_state, n);
+
+  rpo_elim avail (entry->dest);
+  rpo_avail = &avail;
+
+  /* Verify we have no extra entries into the region.  */
+  if (flag_checking && do_region)
+    {
+      auto_bb_flag bb_in_region (fn);
+      for (int i = 0; i < n; ++i)
        {
-         fprintf (dump_file, "Removing dead stmt ");
-         print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
+         basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[i]);
+         bb->flags |= bb_in_region;
        }
-
-      gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
-      if (gimple_code (stmt) == GIMPLE_PHI)
-       remove_phi_node (&gsi, true);
-      else
+      /* We can't merge the first two loops because we cannot rely
+         on EDGE_DFS_BACK for edges not within the region.  But if
+        we decide to always have the bb_in_region flag we can
+        do the checking during the RPO walk itself (but then it's
+        also easy to handle MEME conservatively).  */
+      for (int i = 0; i < n; ++i)
        {
-         basic_block bb = gimple_bb (stmt);
-         unlink_stmt_vdef (stmt);
-         if (gsi_remove (&gsi, true))
-           bitmap_set_bit (el.need_eh_cleanup, bb->index);
-         if (is_gimple_call (stmt) && stmt_can_make_abnormal_goto (stmt))
-           bitmap_set_bit (el.need_ab_cleanup, bb->index);
-         release_defs (stmt);
+         basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[i]);
+         edge e;
+         edge_iterator ei;
+         FOR_EACH_EDGE (e, ei, bb->preds)
+           gcc_assert (e == entry
+                       || (skip_entry_phis && bb == entry->dest)
+                       || (e->src->flags & bb_in_region));
        }
+      for (int i = 0; i < n; ++i)
+       {
+         basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[i]);
+         bb->flags &= ~bb_in_region;
+       }
+    }
 
-      /* Removing a stmt may expose a forwarder block.  */
-      el.el_todo |= TODO_cleanup_cfg;
+  /* Create the VN state.  For the initial size of the various hashtables
+     use a heuristic based on region size and number of SSA names.  */
+  unsigned region_size = (((unsigned HOST_WIDE_INT)n * num_ssa_names)
+                         / (n_basic_blocks_for_fn (fn) - NUM_FIXED_BLOCKS));
+  VN_TOP = create_tmp_var_raw (void_type_node, "vn_top");
+  next_value_id = 1;
+
+  vn_ssa_aux_hash = new hash_table <vn_ssa_aux_hasher> (region_size * 2);
+  gcc_obstack_init (&vn_ssa_aux_obstack);
+
+  gcc_obstack_init (&vn_tables_obstack);
+  gcc_obstack_init (&vn_tables_insert_obstack);
+  valid_info = XCNEW (struct vn_tables_s);
+  allocate_vn_table (valid_info, region_size);
+  last_inserted_ref = NULL;
+  last_inserted_phi = NULL;
+  last_inserted_nary = NULL;
+
+  vn_valueize = rpo_vn_valueize;
+
+  /* Initialize the unwind state and edge/BB executable state.  */
+  bool need_max_rpo_iterate = false;
+  for (int i = 0; i < n; ++i)
+    {
+      basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[i]);
+      rpo_state[i].visited = 0;
+      rpo_state[i].max_rpo = i;
+      bb->flags &= ~BB_EXECUTABLE;
+      bool has_backedges = false;
+      edge e;
+      edge_iterator ei;
+      FOR_EACH_EDGE (e, ei, bb->preds)
+       {
+         if (e->flags & EDGE_DFS_BACK)
+           has_backedges = true;
+         e->flags &= ~EDGE_EXECUTABLE;
+         if (iterate || e == entry || (skip_entry_phis && bb == entry->dest))
+           continue;
+         if (bb_to_rpo[e->src->index] > i)
+           {
+             rpo_state[i].max_rpo = MAX (rpo_state[i].max_rpo,
+                                         bb_to_rpo[e->src->index]);
+             need_max_rpo_iterate = true;
+           }
+         else
+           rpo_state[i].max_rpo
+             = MAX (rpo_state[i].max_rpo,
+                    rpo_state[bb_to_rpo[e->src->index]].max_rpo);
+       }
+      rpo_state[i].iterate = iterate && has_backedges;
+    }
+  entry->flags |= EDGE_EXECUTABLE;
+  entry->dest->flags |= BB_EXECUTABLE;
+
+  /* When there are irreducible regions the simplistic max_rpo computation
+     above for the case of backedges doesn't work and we need to iterate
+     until there are no more changes.  */
+  unsigned nit = 0;
+  while (need_max_rpo_iterate)
+    {
+      nit++;
+      need_max_rpo_iterate = false;
+      for (int i = 0; i < n; ++i)
+       {
+         basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[i]);
+         edge e;
+         edge_iterator ei;
+         FOR_EACH_EDGE (e, ei, bb->preds)
+           {
+             if (e == entry || (skip_entry_phis && bb == entry->dest))
+               continue;
+             int max_rpo = MAX (rpo_state[i].max_rpo,
+                                rpo_state[bb_to_rpo[e->src->index]].max_rpo);
+             if (rpo_state[i].max_rpo != max_rpo)
+               {
+                 rpo_state[i].max_rpo = max_rpo;
+                 need_max_rpo_iterate = true;
+               }
+           }
+       }
     }
+  statistics_histogram_event (cfun, "RPO max_rpo iterations", nit);
 
-  /* Fixup stmts that became noreturn calls.  This may require splitting
-     blocks and thus isn't possible during the dominator walk.  Do this
-     in reverse order so we don't inadvertedly remove a stmt we want to
-     fixup by visiting a dominating now noreturn call first.  */
-  while (!el.to_fixup.is_empty ())
+  /* As heuristic to improve compile-time we handle only the N innermost
+     loops and the outermost one optimistically.  */
+  if (iterate)
     {
-      gimple *stmt = el.to_fixup.pop ();
+      loop_p loop;
+      unsigned max_depth = PARAM_VALUE (PARAM_RPO_VN_MAX_LOOP_DEPTH);
+      FOR_EACH_LOOP (loop, LI_ONLY_INNERMOST)
+       if (loop_depth (loop) > max_depth)
+         for (unsigned i = 2;
+              i < loop_depth (loop) - max_depth; ++i)
+           {
+             basic_block header = superloop_at_depth (loop, i)->header;
+             bool non_latch_backedge = false;
+             edge e;
+             edge_iterator ei;
+             FOR_EACH_EDGE (e, ei, header->preds)
+               if (e->flags & EDGE_DFS_BACK)
+                 {
+                   /* There can be a non-latch backedge into the header
+                      which is part of an outer irreducible region.  We
+                      cannot avoid iterating this block then.  */
+                   if (!dominated_by_p (CDI_DOMINATORS,
+                                        e->src, e->dest))
+                     {
+                       if (dump_file && (dump_flags & TDF_DETAILS))
+                         fprintf (dump_file, "non-latch backedge %d -> %d "
+                                  "forces iteration of loop %d\n",
+                                  e->src->index, e->dest->index, loop->num);
+                       non_latch_backedge = true;
+                     }
+                   else
+                     e->flags |= EDGE_EXECUTABLE;
+                 }
+             rpo_state[bb_to_rpo[header->index]].iterate = non_latch_backedge;
+           }
+    }
 
-      if (dump_file && (dump_flags & TDF_DETAILS))
+  uint64_t nblk = 0;
+  int idx = 0;
+  if (iterate)
+    /* Go and process all blocks, iterating as necessary.  */
+    do
+      {
+       basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[idx]);
+
+       /* If the block has incoming backedges remember unwind state.  This
+          is required even for non-executable blocks since in irreducible
+          regions we might reach them via the backedge and re-start iterating
+          from there.
+          Note we can individually mark blocks with incoming backedges to
+          not iterate where we then handle PHIs conservatively.  We do that
+          heuristically to reduce compile-time for degenerate cases.  */
+       if (rpo_state[idx].iterate)
+         {
+           rpo_state[idx].ob_top = obstack_alloc (&vn_tables_obstack, 0);
+           rpo_state[idx].ref_top = last_inserted_ref;
+           rpo_state[idx].phi_top = last_inserted_phi;
+           rpo_state[idx].nary_top = last_inserted_nary;
+         }
+
+       if (!(bb->flags & BB_EXECUTABLE))
+         {
+           if (dump_file && (dump_flags & TDF_DETAILS))
+             fprintf (dump_file, "Block %d: BB%d found not executable\n",
+                      idx, bb->index);
+           idx++;
+           continue;
+         }
+
+       if (dump_file && (dump_flags & TDF_DETAILS))
+         fprintf (dump_file, "Processing block %d: BB%d\n", idx, bb->index);
+       nblk++;
+       todo |= process_bb (avail, bb,
+                           rpo_state[idx].visited != 0,
+                           rpo_state[idx].iterate,
+                           iterate, eliminate, do_region, exit_bbs, false);
+       rpo_state[idx].visited++;
+
+       /* Verify if changed values flow over executable outgoing backedges
+          and those change destination PHI values (that's the thing we
+          can easily verify).  Reduce over all such edges to the farthest
+          away PHI.  */
+       int iterate_to = -1;
+       edge_iterator ei;
+       edge e;
+       FOR_EACH_EDGE (e, ei, bb->succs)
+         if ((e->flags & (EDGE_DFS_BACK|EDGE_EXECUTABLE))
+             == (EDGE_DFS_BACK|EDGE_EXECUTABLE)
+             && rpo_state[bb_to_rpo[e->dest->index]].iterate)
+           {
+             int destidx = bb_to_rpo[e->dest->index];
+             if (!rpo_state[destidx].visited)
+               {
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   fprintf (dump_file, "Unvisited destination %d\n",
+                            e->dest->index);
+                 if (iterate_to == -1 || destidx < iterate_to)
+                   iterate_to = destidx;
+                 continue;
+               }
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file, "Looking for changed values of backedge"
+                        " %d->%d destination PHIs\n",
+                        e->src->index, e->dest->index);
+             vn_context_bb = e->dest;
+             gphi_iterator gsi;
+             for (gsi = gsi_start_phis (e->dest);
+                  !gsi_end_p (gsi); gsi_next (&gsi))
+               {
+                 bool inserted = false;
+                 /* While we'd ideally just iterate on value changes
+                    we CSE PHIs and do that even across basic-block
+                    boundaries.  So even hashtable state changes can
+                    be important (which is roughly equivalent to
+                    PHI argument value changes).  To not excessively
+                    iterate because of that we track whether a PHI
+                    was CSEd to with GF_PLF_1.  */
+                 bool phival_changed;
+                 if ((phival_changed = visit_phi (gsi.phi (),
+                                                  &inserted, false))
+                     || (inserted && gimple_plf (gsi.phi (), GF_PLF_1)))
+                   {
+                     if (!phival_changed
+                         && dump_file && (dump_flags & TDF_DETAILS))
+                       fprintf (dump_file, "PHI was CSEd and hashtable "
+                                "state (changed)\n");
+                     if (iterate_to == -1 || destidx < iterate_to)
+                       iterate_to = destidx;
+                     break;
+                   }
+               }
+             vn_context_bb = NULL;
+           }
+       if (iterate_to != -1)
+         {
+           do_unwind (&rpo_state[iterate_to], iterate_to, avail, bb_to_rpo);
+           idx = iterate_to;
+           if (dump_file && (dump_flags & TDF_DETAILS))
+             fprintf (dump_file, "Iterating to %d BB%d\n",
+                      iterate_to, rpo[iterate_to]);
+           continue;
+         }
+
+       idx++;
+      }
+    while (idx < n);
+
+  else /* !iterate */
+    {
+      /* Process all blocks greedily with a worklist that enforces RPO
+         processing of reachable blocks.  */
+      auto_bitmap worklist;
+      bitmap_set_bit (worklist, 0);
+      while (!bitmap_empty_p (worklist))
        {
-         fprintf (dump_file, "Fixing up noreturn call ");
-         print_gimple_stmt (dump_file, stmt, 0);
+         int idx = bitmap_first_set_bit (worklist);
+         bitmap_clear_bit (worklist, idx);
+         basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[idx]);
+         gcc_assert ((bb->flags & BB_EXECUTABLE)
+                     && !rpo_state[idx].visited);
+
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "Processing block %d: BB%d\n", idx, bb->index);
+
+         /* When we run into predecessor edges where we cannot trust its
+            executable state mark them executable so PHI processing will
+            be conservative.
+            ???  Do we need to force arguments flowing over that edge
+            to be varying or will they even always be?  */
+         edge_iterator ei;
+         edge e;
+         FOR_EACH_EDGE (e, ei, bb->preds)
+           if (!(e->flags & EDGE_EXECUTABLE)
+               && (bb == entry->dest
+                   || (!rpo_state[bb_to_rpo[e->src->index]].visited
+                       && (rpo_state[bb_to_rpo[e->src->index]].max_rpo
+                           >= (int)idx))))
+             {
+               if (dump_file && (dump_flags & TDF_DETAILS))
+                 fprintf (dump_file, "Cannot trust state of predecessor "
+                          "edge %d -> %d, marking executable\n",
+                          e->src->index, e->dest->index);
+               e->flags |= EDGE_EXECUTABLE;
+             }
+
+         nblk++;
+         todo |= process_bb (avail, bb, false, false, false, eliminate,
+                             do_region, exit_bbs,
+                             skip_entry_phis && bb == entry->dest);
+         rpo_state[idx].visited++;
+
+         FOR_EACH_EDGE (e, ei, bb->succs)
+           if ((e->flags & EDGE_EXECUTABLE)
+               && e->dest->index != EXIT_BLOCK
+               && (!do_region || !bitmap_bit_p (exit_bbs, e->dest->index))
+               && !rpo_state[bb_to_rpo[e->dest->index]].visited)
+             bitmap_set_bit (worklist, bb_to_rpo[e->dest->index]);
        }
+    }
 
-      if (fixup_noreturn_call (stmt))
-       el.el_todo |= TODO_cleanup_cfg;
+  /* If statistics or dump file active.  */
+  int nex = 0;
+  unsigned max_visited = 1;
+  for (int i = 0; i < n; ++i)
+    {
+      basic_block bb = BASIC_BLOCK_FOR_FN (fn, rpo[i]);
+      if (bb->flags & BB_EXECUTABLE)
+       nex++;
+      statistics_histogram_event (cfun, "RPO block visited times",
+                                 rpo_state[i].visited);
+      if (rpo_state[i].visited > max_visited)
+       max_visited = rpo_state[i].visited;
+    }
+  unsigned nvalues = 0, navail = 0;
+  for (hash_table<vn_ssa_aux_hasher>::iterator i = vn_ssa_aux_hash->begin ();
+       i != vn_ssa_aux_hash->end (); ++i)
+    {
+      nvalues++;
+      vn_avail *av = (*i)->avail;
+      while (av)
+       {
+         navail++;
+         av = av->next;
+       }
+    }
+  statistics_counter_event (cfun, "RPO blocks", n);
+  statistics_counter_event (cfun, "RPO blocks visited", nblk);
+  statistics_counter_event (cfun, "RPO blocks executable", nex);
+  statistics_histogram_event (cfun, "RPO iterations", 10*nblk / nex);
+  statistics_histogram_event (cfun, "RPO num values", nvalues);
+  statistics_histogram_event (cfun, "RPO num avail", navail);
+  statistics_histogram_event (cfun, "RPO num lattice",
+                             vn_ssa_aux_hash->elements ());
+  if (dump_file && (dump_flags & (TDF_DETAILS|TDF_STATS)))
+    {
+      fprintf (dump_file, "RPO iteration over %d blocks visited %" PRIu64
+              " blocks in total discovering %d executable blocks iterating "
+              "%d.%d times, a block was visited max. %u times\n",
+              n, nblk, nex,
+              (int)((10*nblk / nex)/10), (int)((10*nblk / nex)%10),
+              max_visited);
+      fprintf (dump_file, "RPO tracked %d values available at %d locations "
+              "and %" PRIu64 " lattice elements\n",
+              nvalues, navail, (uint64_t) vn_ssa_aux_hash->elements ());
     }
 
-  bool do_eh_cleanup = !bitmap_empty_p (el.need_eh_cleanup);
-  bool do_ab_cleanup = !bitmap_empty_p (el.need_ab_cleanup);
+  if (eliminate)
+    {
+      /* When !iterate we already performed elimination during the RPO
+         walk.  */
+      if (iterate)
+       {
+         /* Elimination for region-based VN needs to be done within the
+            RPO walk.  */
+         gcc_assert (! do_region);
+         /* Note we can't use avail.walk here because that gets confused
+            by the existing availability and it will be less efficient
+            as well.  */
+         todo |= eliminate_with_rpo_vn (NULL);
+       }
+      else
+       todo |= avail.eliminate_cleanup (do_region);
+    }
 
-  if (do_eh_cleanup)
-    gimple_purge_all_dead_eh_edges (el.need_eh_cleanup);
+  vn_valueize = NULL;
+  rpo_avail = NULL;
 
-  if (do_ab_cleanup)
-    gimple_purge_all_dead_abnormal_call_edges (el.need_ab_cleanup);
+  XDELETEVEC (bb_to_rpo);
+  XDELETEVEC (rpo);
+  XDELETEVEC (rpo_state);
 
-  if (do_eh_cleanup || do_ab_cleanup)
-    el.el_todo |= TODO_cleanup_cfg;
+  return todo;
+}
 
-  statistics_counter_event (cfun, "Eliminated", el.eliminations);
-  statistics_counter_event (cfun, "Insertions", el.insertions);
+/* Region-based entry for RPO VN.  Performs value-numbering and elimination
+   on the SEME region specified by ENTRY and EXIT_BBS.  If ENTRY is not
+   the only edge into the region at ENTRY->dest PHI nodes in ENTRY->dest
+   are not considered.  */
 
-  return el.el_todo;
+unsigned
+do_rpo_vn (function *fn, edge entry, bitmap exit_bbs)
+{
+  default_vn_walk_kind = VN_WALKREWRITE;
+  unsigned todo = do_rpo_vn (fn, entry, exit_bbs, false, true);
+  free_rpo_vn ();
+  return todo;
 }
 
 
@@ -6057,28 +7236,48 @@ class pass_fre : public gimple_opt_pass
 {
 public:
   pass_fre (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_fre, ctxt)
+    : gimple_opt_pass (pass_data_fre, ctxt), may_iterate (true)
   {}
 
   /* opt_pass methods: */
   opt_pass * clone () { return new pass_fre (m_ctxt); }
-  virtual bool gate (function *) { return flag_tree_fre != 0; }
+  void set_pass_param (unsigned int n, bool param)
+    {
+      gcc_assert (n == 0);
+      may_iterate = param;
+    }
+  virtual bool gate (function *)
+    {
+      return flag_tree_fre != 0 && (may_iterate || optimize > 1);
+    }
   virtual unsigned int execute (function *);
 
+private:
+  bool may_iterate;
 }; // class pass_fre
 
 unsigned int
-pass_fre::execute (function *)
+pass_fre::execute (function *fun)
 {
-  unsigned int todo = 0;
+  unsigned todo = 0;
+
+  /* At -O[1g] use the cheap non-iterating mode.  */
+  bool iterate_p = may_iterate && (optimize > 1);
+  calculate_dominance_info (CDI_DOMINATORS);
+  if (iterate_p)
+    loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
 
-  run_scc_vn (VN_WALKREWRITE);
+  default_vn_walk_kind = VN_WALKREWRITE;
+  todo = do_rpo_vn (fun, NULL, NULL, iterate_p, true);
+  free_rpo_vn ();
 
-  /* Remove all the redundant expressions.  */
-  todo |= vn_eliminate (NULL);
+  if (iterate_p)
+    loop_optimizer_finalize ();
 
-  scc_vn_restore_ssa_info ();
-  free_scc_vn ();
+  /* For late FRE after IVOPTs and unrolling, see if we can
+     remove some TREE_ADDRESSABLE and rewrite stuff into SSA.  */
+  if (!may_iterate)
+    todo |= TODO_update_address_taken;
 
   return todo;
 }
@@ -6090,3 +7289,5 @@ make_pass_fre (gcc::context *ctxt)
 {
   return new pass_fre (ctxt);
 }
+
+#undef BB_EXECUTABLE