]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/tree-ssa-phiopt.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / tree-ssa-phiopt.c
index 43990b796448e99e60591ac3ccdba5a13137d1fa..0e339c46afa29fa97f90d9bc4394370cd9b4b396 100644 (file)
@@ -1,5 +1,5 @@
 /* Optimization of PHI nodes by converting them into straightline code.
-   Copyright (C) 2004-2019 Free Software Foundation, Inc.
+   Copyright (C) 2004-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfghooks.h"
 #include "tree-pass.h"
 #include "ssa.h"
+#include "tree-ssa.h"
 #include "optabs-tree.h"
 #include "insn-config.h"
 #include "gimple-pretty-print.h"
@@ -44,29 +45,35 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-data-ref.h"
 #include "tree-scalar-evolution.h"
 #include "tree-inline.h"
-#include "params.h"
 #include "case-cfn-macros.h"
+#include "tree-eh.h"
+#include "gimple-fold.h"
+#include "internal-fn.h"
+#include "gimple-range.h"
+#include "gimple-match.h"
+#include "dbgcnt.h"
 
 static unsigned int tree_ssa_phiopt_worker (bool, bool, bool);
 static bool two_value_replacement (basic_block, basic_block, edge, gphi *,
                                   tree, tree);
-static bool conditional_replacement (basic_block, basic_block,
-                                    edge, edge, gphi *, tree, tree);
+static bool match_simplify_replacement (basic_block, basic_block,
+                                       edge, edge, gphi *, tree, tree, bool);
 static gphi *factor_out_conditional_conversion (edge, edge, gphi *, tree, tree,
                                                gimple *);
 static int value_replacement (basic_block, basic_block,
-                             edge, edge, gimple *, tree, tree);
+                             edge, edge, gphi *, tree, tree);
 static bool minmax_replacement (basic_block, basic_block,
-                               edge, edge, gimple *, tree, tree);
-static bool abs_replacement (basic_block, basic_block,
-                            edge, edge, gimple *, tree, tree);
-static bool cond_removal_in_popcount_pattern (basic_block, basic_block,
-                                             edge, edge, gimple *, tree, tree);
+                               edge, edge, gphi *, tree, tree);
+static bool spaceship_replacement (basic_block, basic_block,
+                                  edge, edge, gphi *, tree, tree);
+static bool cond_removal_in_builtin_zero_pattern (basic_block, basic_block,
+                                                 edge, edge, gphi *,
+                                                 tree, tree);
 static bool cond_store_replacement (basic_block, basic_block, edge, edge,
                                    hash_set<tree> *);
 static bool cond_if_else_store_replacement (basic_block, basic_block, basic_block);
 static hash_set<tree> * get_non_trapping ();
-static void replace_phi_edge_with_variable (basic_block, edge, gimple *, tree);
+static void replace_phi_edge_with_variable (basic_block, edge, gphi *, tree);
 static void hoist_adjacent_loads (basic_block, basic_block,
                                  basic_block, basic_block);
 static bool gate_hoist_loads (void);
@@ -169,6 +176,8 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
   bool cfgchanged = false;
   hash_set<tree> *nontrap = 0;
 
+  calculate_dominance_info (CDI_DOMINATORS);
+
   if (do_store_elim)
     /* Calculate the set of non-trapping memory accesses.  */
     nontrap = get_non_trapping ();
@@ -334,20 +343,20 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
            }
 
          /* Do the replacement of conditional if it can be done.  */
-         if (two_value_replacement (bb, bb1, e2, phi, arg0, arg1))
-           cfgchanged = true;
-         else if (!early_p
-                  && conditional_replacement (bb, bb1, e1, e2, phi,
-                                              arg0, arg1))
+         if (!early_p && two_value_replacement (bb, bb1, e2, phi, arg0, arg1))
            cfgchanged = true;
-         else if (abs_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
+         else if (match_simplify_replacement (bb, bb1, e1, e2, phi,
+                                              arg0, arg1,
+                                              early_p))
            cfgchanged = true;
          else if (!early_p
-                  && cond_removal_in_popcount_pattern (bb, bb1, e1, e2,
-                                                       phi, arg0, arg1))
+                  && cond_removal_in_builtin_zero_pattern (bb, bb1, e1, e2,
+                                                           phi, arg0, arg1))
            cfgchanged = true;
          else if (minmax_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
            cfgchanged = true;
+         else if (spaceship_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
+           cfgchanged = true;
        }
     }
 
@@ -374,11 +383,40 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
 
 static void
 replace_phi_edge_with_variable (basic_block cond_block,
-                               edge e, gimple *phi, tree new_tree)
+                               edge e, gphi *phi, tree new_tree)
 {
   basic_block bb = gimple_bb (phi);
   basic_block block_to_remove;
   gimple_stmt_iterator gsi;
+  tree phi_result = PHI_RESULT (phi);
+
+  /* Duplicate range info if they are the only things setting the target PHI.
+     This is needed as later on, the new_tree will be replacing
+     The assignement of the PHI.
+     For an example:
+     bb1:
+     _4 = min<a_1, 255>
+     goto bb2
+
+     # RANGE [-INF, 255]
+     a_3 = PHI<_4(1)>
+     bb3:
+
+     use(a_3)
+     And _4 gets propagated into the use of a_3 and losing the range info.
+     This can't be done for more than 2 incoming edges as the propagation
+     won't happen.
+     The new_tree needs to be defined in the same basic block as the conditional.  */
+  if (TREE_CODE (new_tree) == SSA_NAME
+      && EDGE_COUNT (gimple_bb (phi)->preds) == 2
+      && INTEGRAL_TYPE_P (TREE_TYPE (phi_result))
+      && !SSA_NAME_RANGE_INFO (new_tree)
+      && SSA_NAME_RANGE_INFO (phi_result)
+      && gimple_bb (SSA_NAME_DEF_STMT (new_tree)) == cond_block
+      && dbg_cnt (phiopt_edge_range))
+    duplicate_ssa_name_range_info (new_tree,
+                                  SSA_NAME_RANGE_TYPE (phi_result),
+                                  SSA_NAME_RANGE_INFO (phi_result));
 
   /* Change the PHI argument to new.  */
   SET_USE (PHI_ARG_DEF_PTR (phi, e->dest_idx), new_tree);
@@ -407,6 +445,8 @@ replace_phi_edge_with_variable (basic_block cond_block,
   gsi = gsi_last_bb (cond_block);
   gsi_remove (&gsi, true);
 
+  statistics_counter_event (cfun, "Replace PHI with variable", 1);
+
   if (dump_file && (dump_flags & TDF_DETAILS))
     fprintf (dump_file,
              "COND_EXPR in block %d and PHI in block %d converted to straightline code.\n",
@@ -465,6 +505,9 @@ factor_out_conditional_conversion (edge e0, edge e1, gphi *phi,
       if (!is_gimple_reg_type (TREE_TYPE (new_arg0)))
        return NULL;
     }
+  if (TREE_CODE (new_arg0) == SSA_NAME
+      && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_arg0))
+    return NULL;
 
   if (TREE_CODE (arg1) == SSA_NAME)
     {
@@ -475,13 +518,25 @@ factor_out_conditional_conversion (edge e0, edge e1, gphi *phi,
          || gimple_assign_rhs_code (arg1_def_stmt) != convert_code)
        return NULL;
 
+      /* Either arg1_def_stmt or arg0_def_stmt should be conditional.  */
+      if (dominated_by_p (CDI_DOMINATORS, gimple_bb (phi), gimple_bb (arg0_def_stmt))
+         && dominated_by_p (CDI_DOMINATORS,
+                            gimple_bb (phi), gimple_bb (arg1_def_stmt)))
+       return NULL;
+
       /* Use the RHS as new_arg1.  */
       new_arg1 = gimple_assign_rhs1 (arg1_def_stmt);
       if (convert_code == VIEW_CONVERT_EXPR)
        new_arg1 = TREE_OPERAND (new_arg1, 0);
+      if (TREE_CODE (new_arg1) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_arg1))
+       return NULL;
     }
   else
     {
+      /* arg0_def_stmt should be conditional.  */
+      if (dominated_by_p (CDI_DOMINATORS, gimple_bb (phi), gimple_bb (arg0_def_stmt)))
+       return NULL;
       /* If arg1 is an INTEGER_CST, fold it to new type.  */
       if (INTEGRAL_TYPE_P (TREE_TYPE (new_arg0))
          && int_fits_type_p (arg1, TREE_TYPE (new_arg0)))
@@ -591,6 +646,9 @@ factor_out_conditional_conversion (edge e0, edge e1, gphi *phi,
   /* Remove the original PHI stmt.  */
   gsi = gsi_for_stmt (phi);
   gsi_remove (&gsi, true);
+
+  statistics_counter_event (cfun, "factored out cast", 1);
+
   return newphi;
 }
 
@@ -631,7 +689,6 @@ two_value_replacement (basic_block cond_bb, basic_block middle_bb,
 
   if (TREE_CODE (lhs) != SSA_NAME
       || !INTEGRAL_TYPE_P (TREE_TYPE (lhs))
-      || TREE_CODE (TREE_TYPE (lhs)) == BOOLEAN_TYPE
       || TREE_CODE (rhs) != INTEGER_CST)
     return false;
 
@@ -644,9 +701,33 @@ two_value_replacement (basic_block cond_bb, basic_block middle_bb,
       return false;
     }
 
+  /* Defer boolean x ? 0 : {1,-1} or x ? {1,-1} : 0 to
+     match_simplify_replacement.  */
+  if (TREE_CODE (TREE_TYPE (lhs)) == BOOLEAN_TYPE
+      && (integer_zerop (arg0)
+         || integer_zerop (arg1)
+         || TREE_CODE (TREE_TYPE (arg0)) == BOOLEAN_TYPE
+         || (TYPE_PRECISION (TREE_TYPE (arg0))
+             <= TYPE_PRECISION (TREE_TYPE (lhs)))))
+    return false;
+
   wide_int min, max;
-  if (get_range_info (lhs, &min, &max) != VR_RANGE
-      || min + 1 != max
+  value_range r;
+  get_range_query (cfun)->range_of_expr (r, lhs);
+
+  if (r.kind () == VR_RANGE)
+    {
+      min = r.lower_bound ();
+      max = r.upper_bound ();
+    }
+  else
+    {
+      int prec = TYPE_PRECISION (TREE_TYPE (lhs));
+      signop sgn = TYPE_SIGN (TREE_TYPE (lhs));
+      min = wi::min_value (prec, sgn);
+      max = wi::max_value (prec, sgn);
+    }
+  if (min + 1 != max
       || (wi::to_wide (rhs) != min
          && wi::to_wide (rhs) != max))
     return false;
@@ -713,16 +794,16 @@ two_value_replacement (basic_block cond_bb, basic_block middle_bb,
     }
 
   tree arg = wide_int_to_tree (type, a);
-  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
-  if (!useless_type_conversion_p (type, TREE_TYPE (lhs)))
-    lhs = gimplify_build1 (&gsi, NOP_EXPR, type, lhs);
+  gimple_seq stmts = NULL;
+  lhs = gimple_convert (&stmts, type, lhs);
   tree new_rhs;
   if (code == PLUS_EXPR)
-    new_rhs = gimplify_build2 (&gsi, PLUS_EXPR, type, lhs, arg);
+    new_rhs = gimple_build (&stmts, PLUS_EXPR, type, lhs, arg);
   else
-    new_rhs = gimplify_build2 (&gsi, MINUS_EXPR, type, arg, lhs);
-  if (!useless_type_conversion_p (TREE_TYPE (arg0), type))
-    new_rhs = gimplify_build1 (&gsi, NOP_EXPR, TREE_TYPE (arg0), new_rhs);
+    new_rhs = gimple_build (&stmts, MINUS_EXPR, type, arg, lhs);
+  new_rhs = gimple_convert (&stmts, TREE_TYPE (arg0), new_rhs);
+  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+  gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT);
 
   replace_phi_edge_with_variable (cond_bb, e1, phi, new_rhs);
 
@@ -730,119 +811,242 @@ two_value_replacement (basic_block cond_bb, basic_block middle_bb,
   return true;
 }
 
-/*  The function conditional_replacement does the main work of doing the
-    conditional replacement.  Return true if the replacement is done.
+/* Return TRUE if SEQ/OP pair should be allowed during early phiopt.
+   Currently this is to allow MIN/MAX and ABS/NEGATE and constants.  */
+static bool
+phiopt_early_allow (gimple_seq &seq, gimple_match_op &op)
+{
+  /* Don't allow functions. */
+  if (!op.code.is_tree_code ())
+    return false;
+  tree_code code = (tree_code)op.code;
+
+  /* For non-empty sequence, only allow one statement.  */
+  if (!gimple_seq_empty_p (seq))
+    {
+      /* Check to make sure op was already a SSA_NAME.  */
+      if (code != SSA_NAME)
+       return false;
+      if (!gimple_seq_singleton_p (seq))
+       return false;
+      gimple *stmt = gimple_seq_first_stmt (seq);
+      /* Only allow assignments.  */
+      if (!is_gimple_assign (stmt))
+       return false;
+      if (gimple_assign_lhs (stmt) != op.ops[0])
+       return false;
+      code = gimple_assign_rhs_code (stmt);
+    }
+
+  switch (code)
+    {
+      case MIN_EXPR:
+      case MAX_EXPR:
+      case ABS_EXPR:
+      case ABSU_EXPR:
+      case NEGATE_EXPR:
+      case SSA_NAME:
+       return true;
+      case INTEGER_CST:
+      case REAL_CST:
+      case VECTOR_CST:
+      case FIXED_CST:
+       return true;
+      default:
+       return false;
+    }
+}
+
+/* gimple_simplify_phiopt is like gimple_simplify but designed for PHIOPT.
+   Return NULL if nothing can be simplified or the resulting simplified value
+   with parts pushed if EARLY_P was true. Also rejects non allowed tree code
+   if EARLY_P is set.
+   Takes the comparison from COMP_STMT and two args, ARG0 and ARG1 and tries
+   to simplify CMP ? ARG0 : ARG1.
+   Also try to simplify (!CMP) ? ARG1 : ARG0 if the non-inverse failed.  */
+static tree
+gimple_simplify_phiopt (bool early_p, tree type, gimple *comp_stmt,
+                       tree arg0, tree arg1,
+                       gimple_seq *seq)
+{
+  tree result;
+  gimple_seq seq1 = NULL;
+  enum tree_code comp_code = gimple_cond_code (comp_stmt);
+  location_t loc = gimple_location (comp_stmt);
+  tree cmp0 = gimple_cond_lhs (comp_stmt);
+  tree cmp1 = gimple_cond_rhs (comp_stmt);
+  /* To handle special cases like floating point comparison, it is easier and
+     less error-prone to build a tree and gimplify it on the fly though it is
+     less efficient.
+     Don't use fold_build2 here as that might create (bool)a instead of just
+     "a != 0".  */
+  tree cond = build2_loc (loc, comp_code, boolean_type_node,
+                         cmp0, cmp1);
+  gimple_match_op op (gimple_match_cond::UNCOND,
+                     COND_EXPR, type, cond, arg0, arg1);
+
+  if (op.resimplify (&seq1, follow_all_ssa_edges))
+    {
+      /* Early we want only to allow some generated tree codes. */
+      if (!early_p
+         || phiopt_early_allow (seq1, op))
+       {
+         result = maybe_push_res_to_seq (&op, &seq1);
+         if (result)
+           {
+             gimple_seq_add_seq_without_update (seq, seq1);
+             return result;
+           }
+       }
+    }
+  gimple_seq_discard (seq1);
+  seq1 = NULL;
+
+  /* Try the inverted comparison, that is !COMP ? ARG1 : ARG0. */
+  comp_code = invert_tree_comparison (comp_code, HONOR_NANS (cmp0));
+
+  if (comp_code == ERROR_MARK)
+    return NULL;
+
+  cond = build2_loc (loc,
+                    comp_code, boolean_type_node,
+                    cmp0, cmp1);
+  gimple_match_op op1 (gimple_match_cond::UNCOND,
+                      COND_EXPR, type, cond, arg1, arg0);
+
+  if (op1.resimplify (&seq1, follow_all_ssa_edges))
+    {
+      /* Early we want only to allow some generated tree codes. */
+      if (!early_p
+         || phiopt_early_allow (seq1, op1))
+       {
+         result = maybe_push_res_to_seq (&op1, &seq1);
+         if (result)
+           {
+             gimple_seq_add_seq_without_update (seq, seq1);
+             return result;
+           }
+       }
+    }
+  gimple_seq_discard (seq1);
+
+  return NULL;
+}
+
+/*  The function match_simplify_replacement does the main work of doing the
+    replacement using match and simplify.  Return true if the replacement is done.
     Otherwise return false.
     BB is the basic block where the replacement is going to be done on.  ARG0
     is argument 0 from PHI.  Likewise for ARG1.  */
 
 static bool
-conditional_replacement (basic_block cond_bb, basic_block middle_bb,
-                        edge e0, edge e1, gphi *phi,
-                        tree arg0, tree arg1)
+match_simplify_replacement (basic_block cond_bb, basic_block middle_bb,
+                           edge e0, edge e1, gphi *phi,
+                           tree arg0, tree arg1, bool early_p)
 {
-  tree result;
   gimple *stmt;
-  gassign *new_stmt;
-  tree cond;
   gimple_stmt_iterator gsi;
   edge true_edge, false_edge;
-  tree new_var, new_var2;
-  bool neg;
-
-  /* FIXME: Gimplification of complex type is too hard for now.  */
-  /* We aren't prepared to handle vectors either (and it is a question
-     if it would be worthwhile anyway).  */
-  if (!(INTEGRAL_TYPE_P (TREE_TYPE (arg0))
-       || POINTER_TYPE_P (TREE_TYPE (arg0)))
-      || !(INTEGRAL_TYPE_P (TREE_TYPE (arg1))
-          || POINTER_TYPE_P (TREE_TYPE (arg1))))
-    return false;
+  gimple_seq seq = NULL;
+  tree result;
+  gimple *stmt_to_move = NULL;
 
-  /* The PHI arguments have the constants 0 and 1, or 0 and -1, then
-     convert it to the conditional.  */
-  if ((integer_zerop (arg0) && integer_onep (arg1))
-      || (integer_zerop (arg1) && integer_onep (arg0)))
-    neg = false;
-  else if ((integer_zerop (arg0) && integer_all_onesp (arg1))
-          || (integer_zerop (arg1) && integer_all_onesp (arg0)))
-    neg = true;
-  else
+  /* Special case A ? B : B as this will always simplify to B. */
+  if (operand_equal_for_phi_arg_p (arg0, arg1))
     return false;
 
+  /* If the basic block only has a cheap preparation statement,
+     allow it and move it once the transformation is done. */
   if (!empty_block_p (middle_bb))
-    return false;
+    {
+      stmt_to_move = last_and_only_stmt (middle_bb);
+      if (!stmt_to_move)
+       return false;
 
-  /* At this point we know we have a GIMPLE_COND with two successors.
-     One successor is BB, the other successor is an empty block which
-     falls through into BB.
+      if (gimple_vuse (stmt_to_move))
+       return false;
 
-     There is a single PHI node at the join point (BB) and its arguments
-     are constants (0, 1) or (0, -1).
+      if (gimple_could_trap_p (stmt_to_move)
+         || gimple_has_side_effects (stmt_to_move))
+       return false;
 
-     So, given the condition COND, and the two PHI arguments, we can
-     rewrite this PHI into non-branching code:
+      if (gimple_uses_undefined_value_p (stmt_to_move))
+       return false;
 
-       dest = (COND) or dest = COND'
+      /* Allow assignments and not no calls.
+        As const calls don't match any of the above, yet they could
+        still have some side-effects - they could contain
+        gimple_could_trap_p statements, like floating point
+        exceptions or integer division by zero.  See PR70586.
+        FIXME: perhaps gimple_has_side_effects or gimple_could_trap_p
+        should handle this.  */
+      if (!is_gimple_assign (stmt_to_move))
+       return false;
 
-     We use the condition as-is if the argument associated with the
-     true edge has the value one or the argument associated with the
-     false edge as the value zero.  Note that those conditions are not
-     the same since only one of the outgoing edges from the GIMPLE_COND
-     will directly reach BB and thus be associated with an argument.  */
+      tree lhs = gimple_assign_lhs (stmt_to_move);
+      gimple *use_stmt;
+      use_operand_p use_p;
 
-  stmt = last_stmt (cond_bb);
-  result = PHI_RESULT (phi);
+      /* Allow only a statement which feeds into the phi.  */
+      if (!lhs || TREE_CODE (lhs) != SSA_NAME
+         || !single_imm_use (lhs, &use_p, &use_stmt)
+         || use_stmt != phi)
+       return false;
+    }
 
-  /* To handle special cases like floating point comparison, it is easier and
-     less error-prone to build a tree and gimplify it on the fly though it is
-     less efficient.  */
-  cond = fold_build2_loc (gimple_location (stmt),
-                         gimple_cond_code (stmt), boolean_type_node,
-                         gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
+    /* At this point we know we have a GIMPLE_COND with two successors.
+     One successor is BB, the other successor is an empty block which
+     falls through into BB.
+
+     There is a single PHI node at the join point (BB).
+
+     So, given the condition COND, and the two PHI arguments, match and simplify
+     can happen on (COND) ? arg0 : arg1. */
+
+  stmt = last_stmt (cond_bb);
 
   /* We need to know which is the true edge and which is the false
      edge so that we know when to invert the condition below.  */
   extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge);
-  if ((e0 == true_edge && integer_zerop (arg0))
-      || (e0 == false_edge && !integer_zerop (arg0))
-      || (e1 == true_edge && integer_zerop (arg1))
-      || (e1 == false_edge && !integer_zerop (arg1)))
-    cond = fold_build1_loc (gimple_location (stmt),
-                            TRUTH_NOT_EXPR, TREE_TYPE (cond), cond);
-
-  if (neg)
-    {
-      cond = fold_convert_loc (gimple_location (stmt),
-                               TREE_TYPE (result), cond);
-      cond = fold_build1_loc (gimple_location (stmt),
-                              NEGATE_EXPR, TREE_TYPE (cond), cond);
-    }
+  if (e1 == true_edge || e0 == false_edge)
+    std::swap (arg0, arg1);
 
-  /* Insert our new statements at the end of conditional block before the
-     COND_STMT.  */
-  gsi = gsi_for_stmt (stmt);
-  new_var = force_gimple_operand_gsi (&gsi, cond, true, NULL, true,
-                                     GSI_SAME_STMT);
+  tree type = TREE_TYPE (gimple_phi_result (phi));
+  result = gimple_simplify_phiopt (early_p, type, stmt,
+                                  arg0, arg1,
+                                  &seq);
+  if (!result)
+    return false;
 
-  if (!useless_type_conversion_p (TREE_TYPE (result), TREE_TYPE (new_var)))
+  gsi = gsi_last_bb (cond_bb);
+  /* Insert the sequence generated from gimple_simplify_phiopt.  */
+  if (seq)
+    gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+
+  /* If there was a statement to move and the result of the statement
+     is going to be used, move it to right before the original
+     conditional.  */
+  if (stmt_to_move
+      && (gimple_assign_lhs (stmt_to_move) == result
+         || !has_single_use (gimple_assign_lhs (stmt_to_move))))
     {
-      location_t locus_0, locus_1;
-
-      new_var2 = make_ssa_name (TREE_TYPE (result));
-      new_stmt = gimple_build_assign (new_var2, CONVERT_EXPR, new_var);
-      gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT);
-      new_var = new_var2;
-
-      /* Set the locus to the first argument, unless is doesn't have one.  */
-      locus_0 = gimple_phi_arg_location (phi, 0);
-      locus_1 = gimple_phi_arg_location (phi, 1);
-      if (locus_0 == UNKNOWN_LOCATION)
-        locus_0 = locus_1;
-      gimple_set_location (new_stmt, locus_0);
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "statement un-sinked:\n");
+         print_gimple_stmt (dump_file, stmt_to_move, 0,
+                          TDF_VOPS|TDF_MEMSYMS);
+       }
+      gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt_to_move);
+      gsi_move_before (&gsi1, &gsi);
+      reset_flow_sensitive_info (gimple_assign_lhs (stmt_to_move));
     }
 
-  replace_phi_edge_with_variable (cond_bb, e1, phi, new_var);
+  replace_phi_edge_with_variable (cond_bb, e1, phi, result);
+
+  /* Add Statistic here even though replace_phi_edge_with_variable already
+     does it as we want to be able to count when match-simplify happens vs
+     the others.  */
+  statistics_counter_event (cfun, "match-simplify PHI replacement", 1);
 
   /* Note that we optimized this PHI.  */
   return true;
@@ -1050,14 +1254,13 @@ absorbing_element_p (tree_code code, tree arg, bool right, tree rval)
 
 static int
 value_replacement (basic_block cond_bb, basic_block middle_bb,
-                  edge e0, edge e1, gimple *phi,
-                  tree arg0, tree arg1)
+                  edge e0, edge e1, gphi *phi, tree arg0, tree arg1)
 {
   gimple_stmt_iterator gsi;
   gimple *cond;
   edge true_edge, false_edge;
   enum tree_code code;
-  bool emtpy_or_with_defined_p = true;
+  bool empty_or_with_defined_p = true;
 
   /* If the type says honor signed zeros we cannot do this
      optimization.  */
@@ -1076,7 +1279,7 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
        {
          if (gimple_code (stmt) != GIMPLE_PREDICT
              && gimple_code (stmt) != GIMPLE_NOP)
-           emtpy_or_with_defined_p = false;
+           empty_or_with_defined_p = false;
          continue;
        }
       /* Now try to adjust arg0 or arg1 according to the computation
@@ -1086,7 +1289,7 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
             && jump_function_from_stmt (&arg0, stmt))
            || (lhs == arg1
                && jump_function_from_stmt (&arg1, stmt)))
-       emtpy_or_with_defined_p = false;
+       empty_or_with_defined_p = false;
     }
 
   cond = last_stmt (cond_bb);
@@ -1138,7 +1341,7 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
       /* If the middle basic block was empty or is defining the
         PHI arguments and this is a single phi where the args are different
         for the edges e0 and e1 then we can remove the middle basic block. */
-      if (emtpy_or_with_defined_p
+      if (empty_or_with_defined_p
          && single_non_singleton_phi_for_edges (phi_nodes (gimple_bb (phi)),
                                                 e0, e1) == phi)
        {
@@ -1148,6 +1351,8 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
        }
       else
        {
+         statistics_counter_event (cfun, "Replace PHI with variable/value_replacement", 1);
+
          /* Replace the PHI arguments with arg. */
          SET_PHI_ARG_DEF (phi, e0->dest_idx, arg);
          SET_PHI_ARG_DEF (phi, e1->dest_idx, arg);
@@ -1256,7 +1461,7 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
       && profile_status_for_fn (cfun) != PROFILE_ABSENT
       && EDGE_PRED (middle_bb, 0)->probability < profile_probability::even ()
       /* If assign is cheap, there is no point avoiding it.  */
-      && estimate_num_insns (bb_seq (middle_bb), &eni_time_weights)
+      && estimate_num_insns_seq (bb_seq (middle_bb), &eni_time_weights)
         >= 3 * estimate_num_insns (cond, &eni_time_weights))
     return 0;
 
@@ -1324,16 +1529,6 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
           <bb 4>:
           # u_3 = PHI <u_6(3), 4294967295(2)>  */
       reset_flow_sensitive_info (lhs);
-      if (INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
-       {
-         /* If available, we can use VR of phi result at least.  */
-         tree phires = gimple_phi_result (phi);
-         struct range_info_def *phires_range_info
-           = SSA_NAME_RANGE_INFO (phires);
-         if (phires_range_info)
-           duplicate_ssa_name_range_info (lhs, SSA_NAME_RANGE_TYPE (phires),
-                                          phires_range_info);
-       }
       gimple_stmt_iterator gsi_from;
       for (int i = prep_cnt - 1; i >= 0; --i)
        {
@@ -1359,30 +1554,28 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
 
 static bool
 minmax_replacement (basic_block cond_bb, basic_block middle_bb,
-                   edge e0, edge e1, gimple *phi,
-                   tree arg0, tree arg1)
+                   edge e0, edge e1, gphi *phi, tree arg0, tree arg1)
 {
-  tree result, type, rhs;
-  gcond *cond;
-  gassign *new_stmt;
+  tree result;
   edge true_edge, false_edge;
-  enum tree_code cmp, minmax, ass_code;
-  tree smaller, alt_smaller, larger, alt_larger, arg_true, arg_false;
+  enum tree_code minmax, ass_code;
+  tree smaller, larger, arg_true, arg_false;
   gimple_stmt_iterator gsi, gsi_from;
 
-  type = TREE_TYPE (PHI_RESULT (phi));
+  tree type = TREE_TYPE (PHI_RESULT (phi));
 
   /* The optimization may be unsafe due to NaNs.  */
   if (HONOR_NANS (type) || HONOR_SIGNED_ZEROS (type))
     return false;
 
-  cond = as_a <gcond *> (last_stmt (cond_bb));
-  cmp = gimple_cond_code (cond);
-  rhs = gimple_cond_rhs (cond);
+  gcond *cond = as_a <gcond *> (last_stmt (cond_bb));
+  enum tree_code cmp = gimple_cond_code (cond);
+  tree rhs = gimple_cond_rhs (cond);
 
   /* Turn EQ/NE of extreme values to order comparisons.  */
   if ((cmp == NE_EXPR || cmp == EQ_EXPR)
-      && TREE_CODE (rhs) == INTEGER_CST)
+      && TREE_CODE (rhs) == INTEGER_CST
+      && INTEGRAL_TYPE_P (TREE_TYPE (rhs)))
     {
       if (wi::eq_p (wi::to_wide (rhs), wi::min_value (TREE_TYPE (rhs))))
        {
@@ -1400,15 +1593,16 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb,
 
   /* This transformation is only valid for order comparisons.  Record which
      operand is smaller/larger if the result of the comparison is true.  */
-  alt_smaller = NULL_TREE;
-  alt_larger = NULL_TREE;
+  tree alt_smaller = NULL_TREE;
+  tree alt_larger = NULL_TREE;
   if (cmp == LT_EXPR || cmp == LE_EXPR)
     {
       smaller = gimple_cond_lhs (cond);
       larger = rhs;
       /* If we have smaller < CST it is equivalent to smaller <= CST-1.
         Likewise smaller <= CST is equivalent to smaller < CST+1.  */
-      if (TREE_CODE (larger) == INTEGER_CST)
+      if (TREE_CODE (larger) == INTEGER_CST
+         && INTEGRAL_TYPE_P (TREE_TYPE (larger)))
        {
          if (cmp == LT_EXPR)
            {
@@ -1436,7 +1630,8 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb,
       larger = gimple_cond_lhs (cond);
       /* If we have larger > CST it is equivalent to larger >= CST+1.
         Likewise larger >= CST is equivalent to larger > CST-1.  */
-      if (TREE_CODE (smaller) == INTEGER_CST)
+      if (TREE_CODE (smaller) == INTEGER_CST
+         && INTEGRAL_TYPE_P (TREE_TYPE (smaller)))
        {
          wi::overflow_type overflow;
          if (cmp == GT_EXPR)
@@ -1460,6 +1655,50 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb,
   else
     return false;
 
+  /* Handle the special case of (signed_type)x < 0 being equivalent
+     to x > MAX_VAL(signed_type) and (signed_type)x >= 0 equivalent
+     to x <= MAX_VAL(signed_type).  */
+  if ((cmp == GE_EXPR || cmp == LT_EXPR)
+      && INTEGRAL_TYPE_P (type)
+      && TYPE_UNSIGNED (type)
+      && integer_zerop (rhs))
+    {
+      tree op = gimple_cond_lhs (cond);
+      if (TREE_CODE (op) == SSA_NAME
+         && INTEGRAL_TYPE_P (TREE_TYPE (op))
+         && !TYPE_UNSIGNED (TREE_TYPE (op)))
+       {
+         gimple *def_stmt = SSA_NAME_DEF_STMT (op);
+         if (gimple_assign_cast_p (def_stmt))
+           {
+             tree op1 = gimple_assign_rhs1 (def_stmt);
+             if (INTEGRAL_TYPE_P (TREE_TYPE (op1))
+                 && TYPE_UNSIGNED (TREE_TYPE (op1))
+                 && (TYPE_PRECISION (TREE_TYPE (op))
+                     == TYPE_PRECISION (TREE_TYPE (op1)))
+                 && useless_type_conversion_p (type, TREE_TYPE (op1)))
+               {
+                 wide_int w1 = wi::max_value (TREE_TYPE (op));
+                 wide_int w2 = wi::add (w1, 1);
+                 if (cmp == LT_EXPR)
+                   {
+                     larger = op1;
+                     smaller = wide_int_to_tree (TREE_TYPE (op1), w1);
+                     alt_smaller = wide_int_to_tree (TREE_TYPE (op1), w2);
+                     alt_larger = NULL_TREE;
+                   }
+                 else
+                   {
+                     smaller = op1;
+                     larger = wide_int_to_tree (TREE_TYPE (op1), w1);
+                     alt_larger = wide_int_to_tree (TREE_TYPE (op1), w2);
+                     alt_smaller = NULL_TREE;
+                   }
+               }
+           }
+       }
+    }
+
   /* We need to know which is the true edge and which is the false
       edge so that we know if have abs or negative abs.  */
   extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge);
@@ -1685,26 +1924,549 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb,
       gsi_move_before (&gsi_from, &gsi);
     }
 
-  /* Create an SSA var to hold the min/max result.  If we're the only
-     things setting the target PHI, then we  can clone the PHI
-     variable.  Otherwise we must create a new one.  */
-  result = PHI_RESULT (phi);
-  if (EDGE_COUNT (gimple_bb (phi)->preds) == 2)
-    result = duplicate_ssa_name (result, NULL);
-  else
-    result = make_ssa_name (TREE_TYPE (result));
-
   /* Emit the statement to compute min/max.  */
-  new_stmt = gimple_build_assign (result, minmax, arg0, arg1);
+  gimple_seq stmts = NULL;
+  tree phi_result = PHI_RESULT (phi);
+  result = gimple_build (&stmts, minmax, TREE_TYPE (phi_result), arg0, arg1);
+
   gsi = gsi_last_bb (cond_bb);
-  gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT);
+  gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
 
   replace_phi_edge_with_variable (cond_bb, e1, phi, result);
 
   return true;
 }
 
-/* Convert
+/* Return true if the only executable statement in BB is a GIMPLE_COND.  */
+
+static bool
+cond_only_block_p (basic_block bb)
+{
+  /* BB must have no executable statements.  */
+  gimple_stmt_iterator gsi = gsi_after_labels (bb);
+  if (phi_nodes (bb))
+    return false;
+  while (!gsi_end_p (gsi))
+    {
+      gimple *stmt = gsi_stmt (gsi);
+      if (is_gimple_debug (stmt))
+       ;
+      else if (gimple_code (stmt) == GIMPLE_NOP
+              || gimple_code (stmt) == GIMPLE_PREDICT
+              || gimple_code (stmt) == GIMPLE_COND)
+       ;
+      else
+       return false;
+      gsi_next (&gsi);
+    }
+  return true;
+}
+
+/* Attempt to optimize (x <=> y) cmp 0 and similar comparisons.
+   For strong ordering <=> try to match something like:
+    <bb 2> :  // cond3_bb (== cond2_bb)
+    if (x_4(D) != y_5(D))
+      goto <bb 3>; [INV]
+    else
+      goto <bb 6>; [INV]
+
+    <bb 3> :  // cond_bb
+    if (x_4(D) < y_5(D))
+      goto <bb 6>; [INV]
+    else
+      goto <bb 4>; [INV]
+
+    <bb 4> :  // middle_bb
+
+    <bb 6> :  // phi_bb
+    # iftmp.0_2 = PHI <1(4), 0(2), -1(3)>
+    _1 = iftmp.0_2 == 0;
+
+   and for partial ordering <=> something like:
+
+    <bb 2> :  // cond3_bb
+    if (a_3(D) == b_5(D))
+      goto <bb 6>; [50.00%]
+    else
+      goto <bb 3>; [50.00%]
+
+    <bb 3> [local count: 536870913]:  // cond2_bb
+    if (a_3(D) < b_5(D))
+      goto <bb 6>; [50.00%]
+    else
+      goto <bb 4>; [50.00%]
+
+    <bb 4> [local count: 268435456]:  // cond_bb
+    if (a_3(D) > b_5(D))
+      goto <bb 6>; [50.00%]
+    else
+      goto <bb 5>; [50.00%]
+
+    <bb 5> [local count: 134217728]:  // middle_bb
+
+    <bb 6> [local count: 1073741824]:  // phi_bb
+    # SR.27_4 = PHI <0(2), -1(3), 1(4), 2(5)>
+    _2 = SR.27_4 > 0;  */
+
+static bool
+spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
+                      edge e0, edge e1, gphi *phi,
+                      tree arg0, tree arg1)
+{
+  tree phires = PHI_RESULT (phi);
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (phires))
+      || TYPE_UNSIGNED (TREE_TYPE (phires))
+      || !tree_fits_shwi_p (arg0)
+      || !tree_fits_shwi_p (arg1)
+      || !IN_RANGE (tree_to_shwi (arg0), -1, 2)
+      || !IN_RANGE (tree_to_shwi (arg1), -1, 2))
+    return false;
+
+  basic_block phi_bb = gimple_bb (phi);
+  gcc_assert (phi_bb == e0->dest && phi_bb == e1->dest);
+  if (!IN_RANGE (EDGE_COUNT (phi_bb->preds), 3, 4))
+    return false;
+
+  use_operand_p use_p;
+  gimple *use_stmt;
+  if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (phires))
+    return false;
+  if (!single_imm_use (phires, &use_p, &use_stmt))
+    return false;
+  enum tree_code cmp;
+  tree lhs, rhs;
+  gimple *orig_use_stmt = use_stmt;
+  tree orig_use_lhs = NULL_TREE;
+  int prec = TYPE_PRECISION (TREE_TYPE (phires));
+  if (is_gimple_assign (use_stmt)
+      && gimple_assign_rhs_code (use_stmt) == BIT_AND_EXPR
+      && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST
+      && (wi::to_wide (gimple_assign_rhs2 (use_stmt))
+         == wi::shifted_mask (1, prec - 1, false, prec)))
+    {
+      /* For partial_ordering result operator>= with unspec as second
+        argument is (res & 1) == res, folded by match.pd into
+        (res & ~1) == 0.  */
+      orig_use_lhs = gimple_assign_lhs (use_stmt);
+      if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (orig_use_lhs))
+       return false;
+      if (EDGE_COUNT (phi_bb->preds) != 4)
+       return false;
+      if (!single_imm_use (orig_use_lhs, &use_p, &use_stmt))
+       return false;
+    }
+  if (gimple_code (use_stmt) == GIMPLE_COND)
+    {
+      cmp = gimple_cond_code (use_stmt);
+      lhs = gimple_cond_lhs (use_stmt);
+      rhs = gimple_cond_rhs (use_stmt);
+    }
+  else if (is_gimple_assign (use_stmt))
+    {
+      if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
+       {
+         cmp = gimple_assign_rhs_code (use_stmt);
+         lhs = gimple_assign_rhs1 (use_stmt);
+         rhs = gimple_assign_rhs2 (use_stmt);
+       }
+      else if (gimple_assign_rhs_code (use_stmt) == COND_EXPR)
+       {
+         tree cond = gimple_assign_rhs1 (use_stmt);
+         if (!COMPARISON_CLASS_P (cond))
+           return false;
+         cmp = TREE_CODE (cond);
+         lhs = TREE_OPERAND (cond, 0);
+         rhs = TREE_OPERAND (cond, 1);
+       }
+      else
+       return false;
+    }
+  else
+    return false;
+  switch (cmp)
+    {
+    case EQ_EXPR:
+    case NE_EXPR:
+    case LT_EXPR:
+    case GT_EXPR:
+    case LE_EXPR:
+    case GE_EXPR:
+      break;
+    default:
+      return false;
+    }
+  if (lhs != (orig_use_lhs ? orig_use_lhs : phires)
+      || !tree_fits_shwi_p (rhs)
+      || !IN_RANGE (tree_to_shwi (rhs), -1, 1))
+    return false;
+  if (orig_use_lhs)
+    {
+      if ((cmp != EQ_EXPR && cmp != NE_EXPR) || !integer_zerop (rhs))
+       return false;
+      /* As for -ffast-math we assume the 2 return to be
+        impossible, canonicalize (res & ~1) == 0 into
+        res >= 0 and (res & ~1) != 0 as res < 0.  */
+      cmp = cmp == EQ_EXPR ? GE_EXPR : LT_EXPR;
+    }
+
+  if (!empty_block_p (middle_bb))
+    return false;
+
+  gcond *cond1 = as_a <gcond *> (last_stmt (cond_bb));
+  enum tree_code cmp1 = gimple_cond_code (cond1);
+  switch (cmp1)
+    {
+    case LT_EXPR:
+    case LE_EXPR:
+    case GT_EXPR:
+    case GE_EXPR:
+      break;
+    default:
+      return false;
+    }
+  tree lhs1 = gimple_cond_lhs (cond1);
+  tree rhs1 = gimple_cond_rhs (cond1);
+  /* The optimization may be unsafe due to NaNs.  */
+  if (HONOR_NANS (TREE_TYPE (lhs1)))
+    return false;
+  if (TREE_CODE (lhs1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs1))
+    return false;
+  if (TREE_CODE (rhs1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1))
+    return false;
+
+  if (!single_pred_p (cond_bb) || !cond_only_block_p (cond_bb))
+    return false;
+
+  basic_block cond2_bb = single_pred (cond_bb);
+  if (EDGE_COUNT (cond2_bb->succs) != 2)
+    return false;
+  edge cond2_phi_edge;
+  if (EDGE_SUCC (cond2_bb, 0)->dest == cond_bb)
+    {
+      if (EDGE_SUCC (cond2_bb, 1)->dest != phi_bb)
+       return false;
+      cond2_phi_edge = EDGE_SUCC (cond2_bb, 1);
+    }
+  else if (EDGE_SUCC (cond2_bb, 0)->dest != phi_bb)
+    return false;
+  else
+    cond2_phi_edge = EDGE_SUCC (cond2_bb, 0);
+  tree arg2 = gimple_phi_arg_def (phi, cond2_phi_edge->dest_idx);
+  if (!tree_fits_shwi_p (arg2))
+    return false;
+  gimple *cond2 = last_stmt (cond2_bb);
+  if (cond2 == NULL || gimple_code (cond2) != GIMPLE_COND)
+    return false;
+  enum tree_code cmp2 = gimple_cond_code (cond2);
+  tree lhs2 = gimple_cond_lhs (cond2);
+  tree rhs2 = gimple_cond_rhs (cond2);
+  if (lhs2 == lhs1)
+    {
+      if (!operand_equal_p (rhs2, rhs1, 0))
+       {
+         if ((cmp2 == EQ_EXPR || cmp2 == NE_EXPR)
+             && TREE_CODE (rhs1) == INTEGER_CST
+             && TREE_CODE (rhs2) == INTEGER_CST)
+           {
+             /* For integers, we can have cond2 x == 5
+                and cond1 x < 5, x <= 4, x <= 5, x < 6,
+                x > 5, x >= 6, x >= 5 or x > 4.  */
+             if (tree_int_cst_lt (rhs1, rhs2))
+               {
+                 if (wi::ne_p (wi::to_wide (rhs1) + 1, wi::to_wide (rhs2)))
+                   return false;
+                 if (cmp1 == LE_EXPR)
+                   cmp1 = LT_EXPR;
+                 else if (cmp1 == GT_EXPR)
+                   cmp1 = GE_EXPR;
+                 else
+                   return false;
+               }
+             else
+               {
+                 gcc_checking_assert (tree_int_cst_lt (rhs2, rhs1));
+                 if (wi::ne_p (wi::to_wide (rhs2) + 1, wi::to_wide (rhs1)))
+                   return false;
+                 if (cmp1 == LT_EXPR)
+                   cmp1 = LE_EXPR;
+                 else if (cmp1 == GE_EXPR)
+                   cmp1 = GT_EXPR;
+                 else
+                   return false;
+               }
+             rhs1 = rhs2;
+           }
+         else
+           return false;
+       }
+    }
+  else if (lhs2 == rhs1)
+    {
+      if (rhs2 != lhs1)
+       return false;
+    }
+  else
+    return false;
+
+  tree arg3 = arg2;
+  basic_block cond3_bb = cond2_bb;
+  edge cond3_phi_edge = cond2_phi_edge;
+  gimple *cond3 = cond2;
+  enum tree_code cmp3 = cmp2;
+  tree lhs3 = lhs2;
+  tree rhs3 = rhs2;
+  if (EDGE_COUNT (phi_bb->preds) == 4)
+    {
+      if (absu_hwi (tree_to_shwi (arg2)) != 1)
+       return false;
+      if (e1->flags & EDGE_TRUE_VALUE)
+       {
+         if (tree_to_shwi (arg0) != 2
+             || absu_hwi (tree_to_shwi (arg1)) != 1
+             || wi::to_widest (arg1) == wi::to_widest (arg2))
+           return false;
+       }
+      else if (tree_to_shwi (arg1) != 2
+              || absu_hwi (tree_to_shwi (arg0)) != 1
+              || wi::to_widest (arg0) == wi::to_widest (arg1))
+       return false;
+      switch (cmp2)
+       {
+       case LT_EXPR:
+       case LE_EXPR:
+       case GT_EXPR:
+       case GE_EXPR:
+         break;
+       default:
+         return false;
+       }
+      /* if (x < y) goto phi_bb; else fallthru;
+        if (x > y) goto phi_bb; else fallthru;
+        bbx:;
+        phi_bb:;
+        is ok, but if x and y are swapped in one of the comparisons,
+        or the comparisons are the same and operands not swapped,
+        or the true and false edges are swapped, it is not.  */
+      if ((lhs2 == lhs1)
+         ^ (((cond2_phi_edge->flags
+              & ((cmp2 == LT_EXPR || cmp2 == LE_EXPR)
+                 ? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) != 0)
+            != ((e1->flags
+                 & ((cmp1 == LT_EXPR || cmp1 == LE_EXPR)
+                    ? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) != 0)))
+       return false;
+      if (!single_pred_p (cond2_bb) || !cond_only_block_p (cond2_bb))
+       return false;
+      cond3_bb = single_pred (cond2_bb);
+      if (EDGE_COUNT (cond2_bb->succs) != 2)
+       return false;
+      if (EDGE_SUCC (cond3_bb, 0)->dest == cond2_bb)
+       {
+         if (EDGE_SUCC (cond3_bb, 1)->dest != phi_bb)
+           return false;
+         cond3_phi_edge = EDGE_SUCC (cond3_bb, 1);
+       }
+      else if (EDGE_SUCC (cond3_bb, 0)->dest != phi_bb)
+       return false;
+      else
+       cond3_phi_edge = EDGE_SUCC (cond3_bb, 0);
+      arg3 = gimple_phi_arg_def (phi, cond3_phi_edge->dest_idx);
+      cond3 = last_stmt (cond3_bb);
+      if (cond3 == NULL || gimple_code (cond3) != GIMPLE_COND)
+       return false;
+      cmp3 = gimple_cond_code (cond3);
+      lhs3 = gimple_cond_lhs (cond3);
+      rhs3 = gimple_cond_rhs (cond3);
+      if (lhs3 == lhs1)
+       {
+         if (!operand_equal_p (rhs3, rhs1, 0))
+           return false;
+       }
+      else if (lhs3 == rhs1)
+       {
+         if (rhs3 != lhs1)
+           return false;
+       }
+      else
+       return false;
+    }
+  else if (absu_hwi (tree_to_shwi (arg0)) != 1
+          || absu_hwi (tree_to_shwi (arg1)) != 1
+          || wi::to_widest (arg0) == wi::to_widest (arg1))
+    return false;
+
+  if (!integer_zerop (arg3) || (cmp3 != EQ_EXPR && cmp3 != NE_EXPR))
+    return false;
+  if ((cond3_phi_edge->flags & (cmp3 == EQ_EXPR
+                               ? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) == 0)
+    return false;
+
+  /* lhs1 one_cmp rhs1 results in phires of 1.  */
+  enum tree_code one_cmp;
+  if ((cmp1 == LT_EXPR || cmp1 == LE_EXPR)
+      ^ (!integer_onep ((e1->flags & EDGE_TRUE_VALUE) ? arg1 : arg0)))
+    one_cmp = LT_EXPR;
+  else
+    one_cmp = GT_EXPR;
+
+  enum tree_code res_cmp;
+  switch (cmp)
+    {
+    case EQ_EXPR:
+      if (integer_zerop (rhs))
+       res_cmp = EQ_EXPR;
+      else if (integer_minus_onep (rhs))
+       res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
+      else if (integer_onep (rhs))
+       res_cmp = one_cmp;
+      else
+       return false;
+      break;
+    case NE_EXPR:
+      if (integer_zerop (rhs))
+       res_cmp = NE_EXPR;
+      else if (integer_minus_onep (rhs))
+       res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
+      else if (integer_onep (rhs))
+       res_cmp = one_cmp == LT_EXPR ? GE_EXPR : LE_EXPR;
+      else
+       return false;
+      break;
+    case LT_EXPR:
+      if (integer_onep (rhs))
+       res_cmp = one_cmp == LT_EXPR ? GE_EXPR : LE_EXPR;
+      else if (integer_zerop (rhs))
+       res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
+      else
+       return false;
+      break;
+    case LE_EXPR:
+      if (integer_zerop (rhs))
+       res_cmp = one_cmp == LT_EXPR ? GE_EXPR : LE_EXPR;
+      else if (integer_minus_onep (rhs))
+       res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
+      else
+       return false;
+      break;
+    case GT_EXPR:
+      if (integer_minus_onep (rhs))
+       res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
+      else if (integer_zerop (rhs))
+       res_cmp = one_cmp;
+      else
+       return false;
+      break;
+    case GE_EXPR:
+      if (integer_zerop (rhs))
+       res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
+      else if (integer_onep (rhs))
+       res_cmp = one_cmp;
+      else
+       return false;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  if (gimple_code (use_stmt) == GIMPLE_COND)
+    {
+      gcond *use_cond = as_a <gcond *> (use_stmt);
+      gimple_cond_set_code (use_cond, res_cmp);
+      gimple_cond_set_lhs (use_cond, lhs1);
+      gimple_cond_set_rhs (use_cond, rhs1);
+    }
+  else if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
+    {
+      gimple_assign_set_rhs_code (use_stmt, res_cmp);
+      gimple_assign_set_rhs1 (use_stmt, lhs1);
+      gimple_assign_set_rhs2 (use_stmt, rhs1);
+    }
+  else
+    {
+      tree cond = build2 (res_cmp, TREE_TYPE (gimple_assign_rhs1 (use_stmt)),
+                         lhs1, rhs1);
+      gimple_assign_set_rhs1 (use_stmt, cond);
+    }
+  update_stmt (use_stmt);
+
+  if (MAY_HAVE_DEBUG_BIND_STMTS)
+    {
+      use_operand_p use_p;
+      imm_use_iterator iter;
+      bool has_debug_uses = false;
+      FOR_EACH_IMM_USE_FAST (use_p, iter, phires)
+       {
+         gimple *use_stmt = USE_STMT (use_p);
+         if (orig_use_lhs && use_stmt == orig_use_stmt)
+           continue;
+         gcc_assert (is_gimple_debug (use_stmt));
+         has_debug_uses = true;
+         break;
+       }
+      if (orig_use_lhs)
+       {
+         if (!has_debug_uses)
+           FOR_EACH_IMM_USE_FAST (use_p, iter, orig_use_lhs)
+             {
+               gimple *use_stmt = USE_STMT (use_p);
+               gcc_assert (is_gimple_debug (use_stmt));
+               has_debug_uses = true;
+             }
+         gimple_stmt_iterator gsi = gsi_for_stmt (orig_use_stmt);
+         tree zero = build_zero_cst (TREE_TYPE (orig_use_lhs));
+         gimple_assign_set_rhs_with_ops (&gsi, INTEGER_CST, zero);
+         update_stmt (orig_use_stmt);
+       }
+
+      if (has_debug_uses)
+       {
+         /* If there are debug uses, emit something like:
+            # DEBUG D#1 => i_2(D) > j_3(D) ? 1 : -1
+            # DEBUG D#2 => i_2(D) == j_3(D) ? 0 : D#1
+            where > stands for the comparison that yielded 1
+            and replace debug uses of phi result with that D#2.
+            Ignore the value of 2, because if NaNs aren't expected,
+            all floating point numbers should be comparable.  */
+         gimple_stmt_iterator gsi = gsi_after_labels (gimple_bb (phi));
+         tree type = TREE_TYPE (phires);
+         tree temp1 = make_node (DEBUG_EXPR_DECL);
+         DECL_ARTIFICIAL (temp1) = 1;
+         TREE_TYPE (temp1) = type;
+         SET_DECL_MODE (temp1, TYPE_MODE (type));
+         tree t = build2 (one_cmp, boolean_type_node, lhs1, rhs2);
+         t = build3 (COND_EXPR, type, t, build_one_cst (type),
+                     build_int_cst (type, -1));
+         gimple *g = gimple_build_debug_bind (temp1, t, phi);
+         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+         tree temp2 = make_node (DEBUG_EXPR_DECL);
+         DECL_ARTIFICIAL (temp2) = 1;
+         TREE_TYPE (temp2) = type;
+         SET_DECL_MODE (temp2, TYPE_MODE (type));
+         t = build2 (EQ_EXPR, boolean_type_node, lhs1, rhs2);
+         t = build3 (COND_EXPR, type, t, build_zero_cst (type), temp1);
+         g = gimple_build_debug_bind (temp2, t, phi);
+         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+         replace_uses_by (phires, temp2);
+         if (orig_use_lhs)
+           replace_uses_by (orig_use_lhs, temp2);
+       }
+    }
+
+  if (orig_use_lhs)
+    {
+      gimple_stmt_iterator gsi = gsi_for_stmt (orig_use_stmt);
+      gsi_remove (&gsi, true);
+    }
+
+  gimple_stmt_iterator psi = gsi_for_stmt (phi);
+  remove_phi_node (&psi, true);
+  statistics_counter_event (cfun, "spaceship replacement", 1);
+
+  return true;
+}
+
+/* Optimize x ? __builtin_fun (x) : C, where C is __builtin_fun (0).
+   Convert
 
    <bb 2>
    if (b_4(D) != 0)
@@ -1730,16 +2492,20 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb,
 
    <bb 4>
    c_12 = PHI <_9(2)>
-*/
+
+   Similarly for __builtin_clz or __builtin_ctz if
+   C?Z_DEFINED_VALUE_AT_ZERO is 2, optab is present and
+   instead of 0 above it uses the value from that macro.  */
 
 static bool
-cond_removal_in_popcount_pattern (basic_block cond_bb, basic_block middle_bb,
-                                 edge e1, edge e2,
-                                 gimple *phi, tree arg0, tree arg1)
+cond_removal_in_builtin_zero_pattern (basic_block cond_bb,
+                                     basic_block middle_bb,
+                                     edge e1, edge e2, gphi *phi,
+                                     tree arg0, tree arg1)
 {
   gimple *cond;
   gimple_stmt_iterator gsi, gsi_from;
-  gimple *popcount;
+  gimple *call;
   gimple *cast = NULL;
   tree lhs, arg;
 
@@ -1757,35 +2523,82 @@ cond_removal_in_popcount_pattern (basic_block cond_bb, basic_block middle_bb,
   gsi_next_nondebug (&gsi);
   if (!gsi_end_p (gsi))
     {
-      popcount = gsi_stmt (gsi);
+      call = gsi_stmt (gsi);
       gsi_next_nondebug (&gsi);
       if (!gsi_end_p (gsi))
        return false;
     }
   else
     {
-      popcount = cast;
+      call = cast;
       cast = NULL;
     }
 
-  /* Check that we have a popcount builtin.  */
-  if (!is_gimple_call (popcount))
+  /* Check that we have a popcount/clz/ctz builtin.  */
+  if (!is_gimple_call (call) || gimple_call_num_args (call) != 1)
+    return false;
+
+  arg = gimple_call_arg (call, 0);
+  lhs = gimple_get_lhs (call);
+
+  if (lhs == NULL_TREE)
     return false;
-  combined_fn cfn = gimple_call_combined_fn (popcount);
+
+  combined_fn cfn = gimple_call_combined_fn (call);
+  internal_fn ifn = IFN_LAST;
+  int val = 0;
   switch (cfn)
     {
+    case CFN_BUILT_IN_BSWAP16:
+    case CFN_BUILT_IN_BSWAP32:
+    case CFN_BUILT_IN_BSWAP64:
+    case CFN_BUILT_IN_BSWAP128:
+    CASE_CFN_FFS:
+    CASE_CFN_PARITY:
     CASE_CFN_POPCOUNT:
       break;
+    CASE_CFN_CLZ:
+      if (INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+       {
+         tree type = TREE_TYPE (arg);
+         if (direct_internal_fn_supported_p (IFN_CLZ, type, OPTIMIZE_FOR_BOTH)
+             && CLZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (type),
+                                           val) == 2)
+           {
+             ifn = IFN_CLZ;
+             break;
+           }
+       }
+      return false;
+    CASE_CFN_CTZ:
+      if (INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+       {
+         tree type = TREE_TYPE (arg);
+         if (direct_internal_fn_supported_p (IFN_CTZ, type, OPTIMIZE_FOR_BOTH)
+             && CTZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (type),
+                                           val) == 2)
+           {
+             ifn = IFN_CTZ;
+             break;
+           }
+       }
+      return false;
+    case CFN_BUILT_IN_CLRSB:
+      val = TYPE_PRECISION (integer_type_node) - 1;
+      break;
+    case CFN_BUILT_IN_CLRSBL:
+      val = TYPE_PRECISION (long_integer_type_node) - 1;
+      break;
+    case CFN_BUILT_IN_CLRSBLL:
+      val = TYPE_PRECISION (long_long_integer_type_node) - 1;
+      break;
     default:
       return false;
     }
 
-  arg = gimple_call_arg (popcount, 0);
-  lhs = gimple_get_lhs (popcount);
-
   if (cast)
     {
-      /* We have a cast stmt feeding popcount builtin.  */
+      /* We have a cast stmt feeding popcount/clz/ctz builtin.  */
       /* Check that we have a cast prior to that.  */
       if (gimple_code (cast) != GIMPLE_ASSIGN
          || !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (cast)))
@@ -1798,7 +2611,7 @@ cond_removal_in_popcount_pattern (basic_block cond_bb, basic_block middle_bb,
 
   cond = last_stmt (cond_bb);
 
-  /* Cond_bb has a check for b_4 [!=|==] 0 before calling the popcount
+  /* Cond_bb has a check for b_4 [!=|==] 0 before calling the popcount/clz/ctz
      builtin.  */
   if (gimple_code (cond) != GIMPLE_COND
       || (gimple_cond_code (cond) != NE_EXPR
@@ -1818,10 +2631,13 @@ cond_removal_in_popcount_pattern (basic_block cond_bb, basic_block middle_bb,
     }
 
   /* Check PHI arguments.  */
-  if (lhs != arg0 || !integer_zerop (arg1))
+  if (lhs != arg0
+      || TREE_CODE (arg1) != INTEGER_CST
+      || wi::to_wide (arg1) != val)
     return false;
 
-  /* And insert the popcount builtin and cast stmt before the cond_bb.  */
+  /* And insert the popcount/clz/ctz builtin and cast stmt before the
+     cond_bb.  */
   gsi = gsi_last_bb (cond_bb);
   if (cast)
     {
@@ -1829,140 +2645,22 @@ cond_removal_in_popcount_pattern (basic_block cond_bb, basic_block middle_bb,
       gsi_move_before (&gsi_from, &gsi);
       reset_flow_sensitive_info (gimple_get_lhs (cast));
     }
-  gsi_from = gsi_for_stmt (popcount);
-  gsi_move_before (&gsi_from, &gsi);
-  reset_flow_sensitive_info (gimple_get_lhs (popcount));
-
-  /* Now update the PHI and remove unneeded bbs.  */
-  replace_phi_edge_with_variable (cond_bb, e2, phi, lhs);
-  return true;
-}
-
-/*  The function absolute_replacement does the main work of doing the absolute
-    replacement.  Return true if the replacement is done.  Otherwise return
-    false.
-    bb is the basic block where the replacement is going to be done on.  arg0
-    is argument 0 from the phi.  Likewise for arg1.  */
-
-static bool
-abs_replacement (basic_block cond_bb, basic_block middle_bb,
-                edge e0 ATTRIBUTE_UNUSED, edge e1,
-                gimple *phi, tree arg0, tree arg1)
-{
-  tree result;
-  gassign *new_stmt;
-  gimple *cond;
-  gimple_stmt_iterator gsi;
-  edge true_edge, false_edge;
-  gimple *assign;
-  edge e;
-  tree rhs, lhs;
-  bool negate;
-  enum tree_code cond_code;
-
-  /* If the type says honor signed zeros we cannot do this
-     optimization.  */
-  if (HONOR_SIGNED_ZEROS (arg1))
-    return false;
-
-  /* OTHER_BLOCK must have only one executable statement which must have the
-     form arg0 = -arg1 or arg1 = -arg0.  */
-
-  assign = last_and_only_stmt (middle_bb);
-  /* If we did not find the proper negation assignment, then we cannot
-     optimize.  */
-  if (assign == NULL)
-    return false;
-
-  /* If we got here, then we have found the only executable statement
-     in OTHER_BLOCK.  If it is anything other than arg = -arg1 or
-     arg1 = -arg0, then we cannot optimize.  */
-  if (gimple_code (assign) != GIMPLE_ASSIGN)
-    return false;
-
-  lhs = gimple_assign_lhs (assign);
-
-  if (gimple_assign_rhs_code (assign) != NEGATE_EXPR)
-    return false;
-
-  rhs = gimple_assign_rhs1 (assign);
-
-  /* The assignment has to be arg0 = -arg1 or arg1 = -arg0.  */
-  if (!(lhs == arg0 && rhs == arg1)
-      && !(lhs == arg1 && rhs == arg0))
-    return false;
-
-  cond = last_stmt (cond_bb);
-  result = PHI_RESULT (phi);
-
-  /* Only relationals comparing arg[01] against zero are interesting.  */
-  cond_code = gimple_cond_code (cond);
-  if (cond_code != GT_EXPR && cond_code != GE_EXPR
-      && cond_code != LT_EXPR && cond_code != LE_EXPR)
-    return false;
-
-  /* Make sure the conditional is arg[01] OP y.  */
-  if (gimple_cond_lhs (cond) != rhs)
-    return false;
-
-  if (FLOAT_TYPE_P (TREE_TYPE (gimple_cond_rhs (cond)))
-              ? real_zerop (gimple_cond_rhs (cond))
-              : integer_zerop (gimple_cond_rhs (cond)))
-    ;
+  gsi_from = gsi_for_stmt (call);
+  if (ifn == IFN_LAST || gimple_call_internal_p (call))
+    gsi_move_before (&gsi_from, &gsi);
   else
-    return false;
-
-  /* We need to know which is the true edge and which is the false
-     edge so that we know if have abs or negative abs.  */
-  extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge);
-
-  /* For GT_EXPR/GE_EXPR, if the true edge goes to OTHER_BLOCK, then we
-     will need to negate the result.  Similarly for LT_EXPR/LE_EXPR if
-     the false edge goes to OTHER_BLOCK.  */
-  if (cond_code == GT_EXPR || cond_code == GE_EXPR)
-    e = true_edge;
-  else
-    e = false_edge;
-
-  if (e->dest == middle_bb)
-    negate = true;
-  else
-    negate = false;
-
-  /* If the code negates only iff positive then make sure to not
-     introduce undefined behavior when negating or computing the absolute.
-     ???  We could use range info if present to check for arg1 == INT_MIN.  */
-  if (negate
-      && (ANY_INTEGRAL_TYPE_P (TREE_TYPE (arg1))
-         && ! TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg1))))
-    return false;
-
-  result = duplicate_ssa_name (result, NULL);
-
-  if (negate)
-    lhs = make_ssa_name (TREE_TYPE (result));
-  else
-    lhs = result;
-
-  /* Build the modify expression with abs expression.  */
-  new_stmt = gimple_build_assign (lhs, ABS_EXPR, rhs);
-
-  gsi = gsi_last_bb (cond_bb);
-  gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT);
-
-  if (negate)
     {
-      /* Get the right GSI.  We want to insert after the recently
-        added ABS_EXPR statement (which we know is the first statement
-        in the block.  */
-      new_stmt = gimple_build_assign (result, NEGATE_EXPR, lhs);
-
-      gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT);
+      /* For __builtin_c[lt]z* force .C[LT]Z ifn, because only
+        the latter is well defined at zero.  */
+      call = gimple_build_call_internal (ifn, 1, gimple_call_arg (call, 0));
+      gimple_call_set_lhs (call, lhs);
+      gsi_insert_before (&gsi, call, GSI_SAME_STMT);
+      gsi_remove (&gsi_from, true);
     }
+  reset_flow_sensitive_info (lhs);
 
-  replace_phi_edge_with_variable (cond_bb, e1, phi, result);
-
-  /* Note that we optimized this PHI.  */
+  /* Now update the PHI and remove unneeded bbs.  */
+  replace_phi_edge_with_variable (cond_bb, e2, phi, lhs);
   return true;
 }
 
@@ -1983,26 +2681,33 @@ abs_replacement (basic_block cond_bb, basic_block middle_bb,
 
    ??? We currently are very conservative and assume that a load might
    trap even if a store doesn't (write-only memory).  This probably is
-   overly conservative.  */
+   overly conservative.
+
+   We currently support a special case that for !TREE_ADDRESSABLE automatic
+   variables, it could ignore whether something is a load or store because the
+   local stack should be always writable.  */
 
-/* A hash-table of SSA_NAMEs, and in which basic block an MEM_REF
-   through it was seen, which would constitute a no-trap region for
-   same accesses.  */
-struct name_to_bb
+/* A hash-table of references (MEM_REF/ARRAY_REF/COMPONENT_REF), and in which
+   basic block an *_REF through it was seen, which would constitute a
+   no-trap region for same accesses.
+
+   Size is needed to support 2 MEM_REFs of different types, like
+   MEM<double>(s_1) and MEM<long>(s_1), which would compare equal with
+   OEP_ADDRESS_OF.  */
+struct ref_to_bb
 {
-  unsigned int ssa_name_ver;
+  tree exp;
+  HOST_WIDE_INT size;
   unsigned int phase;
-  bool store;
-  HOST_WIDE_INT offset, size;
   basic_block bb;
 };
 
 /* Hashtable helpers.  */
 
-struct ssa_names_hasher : free_ptr_hash <name_to_bb>
+struct refs_hasher : free_ptr_hash<ref_to_bb>
 {
-  static inline hashval_t hash (const name_to_bb *);
-  static inline bool equal (const name_to_bb *, const name_to_bb *);
+  static inline hashval_t hash (const ref_to_bb *);
+  static inline bool equal (const ref_to_bb *, const ref_to_bb *);
 };
 
 /* Used for quick clearing of the hash-table when we see calls.
@@ -2012,28 +2717,29 @@ static unsigned int nt_call_phase;
 /* The hash function.  */
 
 inline hashval_t
-ssa_names_hasher::hash (const name_to_bb *n)
+refs_hasher::hash (const ref_to_bb *n)
 {
-  return n->ssa_name_ver ^ (((hashval_t) n->store) << 31)
-         ^ (n->offset << 6) ^ (n->size << 3);
+  inchash::hash hstate;
+  inchash::add_expr (n->exp, hstate, OEP_ADDRESS_OF);
+  hstate.add_hwi (n->size);
+  return hstate.end ();
 }
 
 /* The equality function of *P1 and *P2.  */
 
 inline bool
-ssa_names_hasher::equal (const name_to_bb *n1, const name_to_bb *n2)
+refs_hasher::equal (const ref_to_bb *n1, const ref_to_bb *n2)
 {
-  return n1->ssa_name_ver == n2->ssa_name_ver
-         && n1->store == n2->store
-         && n1->offset == n2->offset
-         && n1->size == n2->size;
+  return operand_equal_p (n1->exp, n2->exp, OEP_ADDRESS_OF)
+        && n1->size == n2->size;
 }
 
 class nontrapping_dom_walker : public dom_walker
 {
 public:
   nontrapping_dom_walker (cdi_direction direction, hash_set<tree> *ps)
-    : dom_walker (direction), m_nontrapping (ps), m_seen_ssa_names (128) {}
+    : dom_walker (direction), m_nontrapping (ps), m_seen_refs (128)
+  {}
 
   virtual edge before_dom_children (basic_block);
   virtual void after_dom_children (basic_block);
@@ -2050,7 +2756,7 @@ private:
   hash_set<tree> *m_nontrapping;
 
   /* The hash table for remembering what we've seen.  */
-  hash_table<ssa_names_hasher> m_seen_ssa_names;
+  hash_table<refs_hasher> m_seen_refs;
 };
 
 /* Called by walk_dominator_tree, when entering the block BB.  */
@@ -2099,65 +2805,68 @@ nontrapping_dom_walker::after_dom_children (basic_block bb)
 }
 
 /* We see the expression EXP in basic block BB.  If it's an interesting
-   expression (an MEM_REF through an SSA_NAME) possibly insert the
-   expression into the set NONTRAP or the hash table of seen expressions.
-   STORE is true if this expression is on the LHS, otherwise it's on
-   the RHS.  */
+   expression of:
+     1) MEM_REF
+     2) ARRAY_REF
+     3) COMPONENT_REF
+   possibly insert the expression into the set NONTRAP or the hash table
+   of seen expressions.  STORE is true if this expression is on the LHS,
+   otherwise it's on the RHS.  */
 void
 nontrapping_dom_walker::add_or_mark_expr (basic_block bb, tree exp, bool store)
 {
   HOST_WIDE_INT size;
 
-  if (TREE_CODE (exp) == MEM_REF
-      && TREE_CODE (TREE_OPERAND (exp, 0)) == SSA_NAME
-      && tree_fits_shwi_p (TREE_OPERAND (exp, 1))
+  if ((TREE_CODE (exp) == MEM_REF || TREE_CODE (exp) == ARRAY_REF
+       || TREE_CODE (exp) == COMPONENT_REF)
       && (size = int_size_in_bytes (TREE_TYPE (exp))) > 0)
     {
-      tree name = TREE_OPERAND (exp, 0);
-      struct name_to_bb map;
-      name_to_bb **slot;
-      struct name_to_bb *n2bb;
+      struct ref_to_bb map;
+      ref_to_bb **slot;
+      struct ref_to_bb *r2bb;
       basic_block found_bb = 0;
 
-      /* Try to find the last seen MEM_REF through the same
-         SSA_NAME, which can trap.  */
-      map.ssa_name_ver = SSA_NAME_VERSION (name);
-      map.phase = 0;
-      map.bb = 0;
-      map.store = store;
-      map.offset = tree_to_shwi (TREE_OPERAND (exp, 1));
-      map.size = size;
+      if (!store)
+       {
+         tree base = get_base_address (exp);
+         /* Only record a LOAD of a local variable without address-taken, as
+            the local stack is always writable.  This allows cselim on a STORE
+            with a dominating LOAD.  */
+         if (!auto_var_p (base) || TREE_ADDRESSABLE (base))
+           return;
+       }
 
-      slot = m_seen_ssa_names.find_slot (&map, INSERT);
-      n2bb = *slot;
-      if (n2bb && n2bb->phase >= nt_call_phase)
-        found_bb = n2bb->bb;
+      /* Try to find the last seen *_REF, which can trap.  */
+      map.exp = exp;
+      map.size = size;
+      slot = m_seen_refs.find_slot (&map, INSERT);
+      r2bb = *slot;
+      if (r2bb && r2bb->phase >= nt_call_phase)
+       found_bb = r2bb->bb;
 
-      /* If we've found a trapping MEM_REF, _and_ it dominates EXP
-         (it's in a basic block on the path from us to the dominator root)
+      /* If we've found a trapping *_REF, _and_ it dominates EXP
+        (it's in a basic block on the path from us to the dominator root)
         then we can't trap.  */
       if (found_bb && (((size_t)found_bb->aux) & 1) == 1)
        {
          m_nontrapping->add (exp);
        }
       else
-        {
+       {
          /* EXP might trap, so insert it into the hash table.  */
-         if (n2bb)
+         if (r2bb)
            {
-             n2bb->phase = nt_call_phase;
-             n2bb->bb = bb;
+             r2bb->phase = nt_call_phase;
+             r2bb->bb = bb;
            }
          else
            {
-             n2bb = XNEW (struct name_to_bb);
-             n2bb->ssa_name_ver = SSA_NAME_VERSION (name);
-             n2bb->phase = nt_call_phase;
-             n2bb->bb = bb;
-             n2bb->store = store;
-             n2bb->offset = map.offset;
-             n2bb->size = size;
-             *slot = n2bb;
+             r2bb = XNEW (struct ref_to_bb);
+             r2bb->phase = nt_call_phase;
+             r2bb->bb = bb;
+             r2bb->exp = exp;
+             r2bb->size = size;
+             *slot = r2bb;
            }
        }
     }
@@ -2172,9 +2881,6 @@ get_non_trapping (void)
 {
   nt_call_phase = 0;
   hash_set<tree> *nontrap = new hash_set<tree>;
-  /* We're going to do a dominator walk, so ensure that we have
-     dominance information.  */
-  calculate_dominance_info (CDI_DOMINATORS);
 
   nontrapping_dom_walker (CDI_DOMINATORS, nontrap)
     .walk (cfun->cfg->x_entry_block_ptr);
@@ -2224,9 +2930,8 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   locus = gimple_location (assign);
   lhs = gimple_assign_lhs (assign);
   rhs = gimple_assign_rhs1 (assign);
-  if ((TREE_CODE (lhs) != MEM_REF
-       && TREE_CODE (lhs) != ARRAY_REF
-       && TREE_CODE (lhs) != COMPONENT_REF)
+  if ((!REFERENCE_CLASS_P (lhs)
+       && !DECL_P (lhs))
       || !is_gimple_reg_type (TREE_TYPE (lhs)))
     return false;
 
@@ -2235,10 +2940,13 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
      whose value is not available readily, which we want to avoid.  */
   if (!nontrap->contains (lhs))
     {
-      /* If LHS is a local variable without address-taken, we could
+      /* If LHS is an access to a local variable without address-taken
+        (or when we allow data races) and known not to trap, we could
         always safely move down the store.  */
       tree base = get_base_address (lhs);
-      if (!auto_var_p (base) || TREE_ADDRESSABLE (base))
+      if (!auto_var_p (base)
+         || (TREE_ADDRESSABLE (base) && !flag_store_data_races)
+         || tree_could_trap_p (lhs))
        return false;
     }
 
@@ -2270,6 +2978,13 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   name = make_temp_ssa_name (TREE_TYPE (lhs), NULL, "cstore");
   new_stmt = gimple_build_assign (name, lhs);
   gimple_set_location (new_stmt, locus);
+  lhs = unshare_expr (lhs);
+  {
+    /* Set the no-warning bit on the rhs of the load to avoid uninit
+       warnings.  */
+    tree rhs1 = gimple_assign_rhs1 (new_stmt);
+    suppress_warning (rhs1, OPT_Wuninitialized);
+  }
   gsi_insert_on_edge (e1, new_stmt);
 
   /* 3) Create a PHI node at the join block, with one argument
@@ -2280,7 +2995,6 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   add_phi_arg (newphi, rhs, e0, locus);
   add_phi_arg (newphi, name, e1, locus);
 
-  lhs = unshare_expr (lhs);
   new_stmt = gimple_build_assign (lhs, PHI_RESULT (newphi));
 
   /* 4) Insert that PHI node.  */
@@ -2300,6 +3014,7 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
       fprintf (dump_file, "\nInserted a new PHI statement in joint block:\n");
       print_gimple_stmt (dump_file, new_stmt, 0, TDF_VOPS|TDF_MEMSYMS);
     }
+  statistics_counter_event (cfun, "conditional store replacement", 1);
 
   return true;
 }
@@ -2374,6 +3089,8 @@ cond_if_else_store_replacement_1 (basic_block then_bb, basic_block else_bb,
   else
     gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT);
 
+  statistics_counter_event (cfun, "if-then-else store replacement", 1);
+
   return true;
 }
 
@@ -2787,6 +3504,7 @@ hoist_adjacent_loads (basic_block bb0, basic_block bb1,
       gsi_move_to_bb_end (&gsi2, bb0);
       gsi2 = gsi_for_stmt (def2);
       gsi_move_to_bb_end (&gsi2, bb0);
+      statistics_counter_event (cfun, "hoisted loads", 1);
 
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
@@ -2819,7 +3537,7 @@ gate_hoist_loads (void)
    Conditional Replacement
    -----------------------
 
-   This transformation, implemented in conditional_replacement,
+   This transformation, implemented in match_simplify_replacement,
    replaces
 
      bb0:
@@ -2882,7 +3600,7 @@ gate_hoist_loads (void)
    ABS Replacement
    ---------------
 
-   This transformation, implemented in abs_replacement, replaces
+   This transformation, implemented in match_simplify_replacement, replaces
 
      bb0:
        if (a >= 0) goto bb2; else goto bb1;