diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-15.c b/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
index 121f0cff274..8d5fdc233b3 100644
--- a/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
@@ -1,16 +1,14 @@
 /* PR tree-optimization/17506
-   We issue an uninitialized variable warning at a wrong location at
-   line 11, which is very confusing.  Make sure we print out a note to
-   make it less confusing.  (not xfailed alternative)
-   But it is of course ok if we warn in bar about uninitialized use
-   of j.  (not xfailed alternative)  */
+   We used to issue an uninitialized variable warning at a wrong (inlined)
+   location, which was very confusing.  Since early phiopt materializes
+   foo's comparison the warning is issued at the real use in bar, with the note pointing at the declaration.  */
 /* { dg-do compile } */
 /* { dg-options "-O1 -Wuninitialized -ftrivial-auto-var-init=zero" } */
 
 inline int
 foo (int i)
 {
-  if (i) /* { dg-warning "used uninitialized" } */
+  if (i)
     return 1;
   return 0;
 }
@@ -21,6 +19,6 @@ void
 bar (void)
 {
   int j; /* { dg-message "note: 'j' was declared here" "" } */
-  for (; foo (j); ++j)  /* { dg-warning "'j' is used uninitialized" "" { xfail *-*-* } } */
+  for (; foo (j); ++j)  /* { dg-warning "'j' is used uninitialized" } */
     baz ();
 }
diff --git a/gcc/testsuite/gcc.dg/pr109071_6.c b/gcc/testsuite/gcc.dg/pr109071_6.c
index eddf15b350c..6062882583f 100644
--- a/gcc/testsuite/gcc.dg/pr109071_6.c
+++ b/gcc/testsuite/gcc.dg/pr109071_6.c
@@ -1,7 +1,10 @@
 /* PR tree-optimization/109071 need more context for -Warray-bounds warnings
    due to code duplication from jump threading.
    test case is from PR117179, which is a duplication of PR109071.  */  
-/* { dg-options "-O2 -Warray-bounds -fdiagnostics-show-context=1" } */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-show-context=1 -fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled: early phiopt now materializes setval_internal's
+   comparison, so no threaded path with the out-of-bounds range reaches
+   the array access and no warning is issued at all.  */
 /* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
 /* { dg-enable-nn-line-numbers "" } */
 const char* commands[] = {"a", "b"};
diff --git a/gcc/testsuite/gcc.dg/predict-15.c b/gcc/testsuite/gcc.dg/predict-15.c
index 2a8c3ea8597..9a25537c9b6 100644
--- a/gcc/testsuite/gcc.dg/predict-15.c
+++ b/gcc/testsuite/gcc.dg/predict-15.c
@@ -1,5 +1,7 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-profile_estimate" } */
+/* { dg-options "-O2 -fdump-tree-profile_estimate -fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled: early phiopt now materializes the comparison
+   and no goto edge remains to predict.  */
 
 int main(int argc, char **argv)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c
new file mode 100644
index 00000000000..6cc90f656c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c
@@ -0,0 +1,42 @@
+/* Early phiopt materializes a comparison mask "cmp ? CST1 : CST2" as
+   -(int)cmp / (int)cmp, exposing its comparison.  An equality test of the
+   mask ("m == C") then folds to the comparison itself, so the mask
+   disappears and diamonds controlled by the same mask share an identical
+   condition (a prerequisite for pass_merge_diamonds).  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-phiopt1" } */
+
+/* -(int)cmp all-ones mask tested against a constant: the test folds to the
+   comparison.  */
+int
+neg_masktest (int a, int b, int x, int y)
+{
+  int m;
+  if (a > b) m = -1; else m = 0;
+  return (m == -1) ? x : y;
+}
+
+/* (int)cmp boolean mask tested against a constant (the convert case).  */
+int
+cvt_masktest (int a, int b, int x, int y)
+{
+  int m;
+  if (a > b) m = 1; else m = 0;
+  return (m == 1) ? x : y;
+}
+
+/* A mask consumed by other operators is materialized too.  */
+int
+mask_algebra (int a, int b)
+{
+  int m;
+  if (a > b) m = -1; else m = 0;
+  return m & 7;
+}
+
+/* All three masks are materialized: no constant-pair mask PHI survives and
+   the equality tests fold away.  */
+/* { dg-final { scan-tree-dump-not "= PHI <-1\\(" "phiopt1" } } */
+/* { dg-final { scan-tree-dump-not "== -1" "phiopt1" } } */
+/* { dg-final { scan-tree-dump-not "== 1" "phiopt1" } } */
+/* { dg-final { scan-tree-dump "& 7" "phiopt1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c
new file mode 100644
index 00000000000..d3792d4314f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c
@@ -0,0 +1,27 @@
+/* With early phiopt lowering "p != 0 ? 1 : 0" to
+   a boolean mask, the non-null test of a PHI of two object addresses folds
+   away entirely, without needing VRP's predicate folding (compare
+   pr21090.c).  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-thread-jumps -fdelete-null-pointer-checks -fdump-tree-optimized" } */
+
+int g, h;
+
+int
+foo (int a)
+{
+  int *p;
+
+  if (a)
+    p = &g;
+  else
+    p = &h;
+
+  if (p != 0)
+    return 1;
+  else
+    return 0;
+}
+
+/* { dg-final { scan-tree-dump "return 1;" "optimized" { target { ! keeps_null_pointer_checks } } } } */
+/* { dg-final { scan-tree-dump-not "PHI" "optimized" { target { ! keeps_null_pointer_checks } } } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
index 92a87688601..ef755e85270 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
@@ -1,5 +1,8 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */
+/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1 -fdelete-null-pointer-checks -fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled so the non-null test reaches VRP as a branch; early
+   phiopt now lowers it to a boolean mask that is folded even earlier,
+   which pr21090-2.c covers.  */
 
 int g, h;
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c b/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
index 125072941da..3b33961268f 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
@@ -1,5 +1,7 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-threadfull1-details -fdisable-tree-ethread" } */
+/* { dg-options "-O2 -fdump-tree-threadfull1-details -fdisable-tree-ethread -fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled: early phiopt now lowers the remove test to a boolean
+   mask and these threading opportunities change shape.  */
 
 extern void abort (void);
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
index fb62e570bed..83f6d98796f 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fno-tree-dominator-opts -fdump-tree-vrp1" } */
+/* { dg-options "-O2 -fno-tree-dominator-opts -fdump-tree-evrp" } */
 
 int f(int x, int y)
 { 
@@ -15,4 +15,4 @@ int f(int x, int y)
 /* We should have computed x ^ y as zero and propagated the result into the
    PHI feeding the result.  */
 
-/* { dg-final { scan-tree-dump "ret_\[0-9\]+ = PHI <\[01\]\\\(\[0-9\]+\\\), \[01\]\\\(\[0-9\]+\\\)>" "vrp1" } } */
+/* { dg-final { scan-tree-dump "ret_\[0-9\]+ = PHI <\[01\]\\\(\[0-9\]+\\\), \[01\]\\\(\[0-9\]+\\\)>" "evrp" } } */
diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
index e12dc7a8b0c..df658b04cfb 100644
--- a/gcc/tree-ssa-phiopt.cc
+++ b/gcc/tree-ssa-phiopt.cc
@@ -666,6 +666,29 @@ phiopt_early_allow (gimple_seq &seq, gimple_match_op &op)
 	  code = gimple_assign_rhs_code (stmt);
 	  return code == MIN_EXPR || code == MAX_EXPR;
 	}
+      /* Accept CMP, (convert)(compare) and -(convert)(compare) when the
+	 generated stmts are only conversions and comparisons, so a mask such as
+	 "cmp ? -1 : 0" / "cmp ? 1 : 0" can be materialized as -(int)cmp /
+	 (int)cmp, exposing its comparison.  This is only the structural test;
+	 match_simplify_replacement additionally keeps user predictors alive
+	 and leaves diamonds that other paths enter alone.  */
+      if (code == NEGATE_EXPR
+	  || CONVERT_EXPR_CODE_P (code)
+	  || TREE_CODE_CLASS (code) == tcc_comparison)
+	{
+	  for (gimple_stmt_iterator gsi = gsi_start (seq); !gsi_end_p (gsi);
+	       gsi_next (&gsi))
+	    {
+	      gimple *s = gsi_stmt (gsi);
+	      if (!is_gimple_assign (s))
+		return false;
+	      tree_code scode = gimple_assign_rhs_code (s);
+	      if (!CONVERT_EXPR_CODE_P (scode)
+		  && TREE_CODE_CLASS (scode) != tcc_comparison)
+		return false;
+	    }
+	  return true;
+	}
       return false;
     }
 
@@ -684,7 +707,8 @@ phiopt_early_allow (gimple_seq &seq, gimple_match_op &op)
       case FIXED_CST:
 	return true;
       default:
-	return false;
+	/* A bare comparison (CMP) is also allowed.  */
+	return TREE_CODE_CLASS (code) == tcc_comparison;
     }
 }
 
@@ -1101,9 +1125,9 @@ match_simplify_replacement (basic_block cond_bb, basic_block middle_bb,
   }
 
   /* For early phiopt, we don't want to lose user generated predictors
-     if the phiopt is converting `if (a)` into `a` as that might
-     be jump threaded later on so we want to keep around the
-     predictors.  */
+     if the phiopt is converting `if (a)` into `a` (or into the new
+     (convert)cmp / -(convert)cmp mask forms) as that might be jump
+     threaded later on so we want to keep around the predictors.  */
   if (early_p && result && TREE_CODE (result) == SSA_NAME)
     {
       bool check_it = false;
@@ -1111,15 +1135,33 @@ match_simplify_replacement (basic_block cond_bb, basic_block middle_bb,
       tree cmp1 = gimple_cond_rhs (stmt);
       if (result == cmp0 || result == cmp1)
 	check_it = true;
-      else if (gimple_seq_singleton_p (seq))
-        {
-	  gimple *stmt = gimple_seq_first_stmt (seq);
-	  if (is_gimple_assign (stmt)
-	      && result == gimple_assign_lhs (stmt)
-	      && TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))
-		   == tcc_comparison)
+      else if (!gimple_seq_empty_p (seq))
+	{
+	  /* A comparison mask: the generated statements are only
+	     conversions, negations and comparisons, with at least one
+	     comparison (the forms phiopt_early_allow accepts).  */
+	  bool has_cmp = false, only_mask_ops = true;
+	  for (gimple_stmt_iterator gi = gsi_start (seq); !gsi_end_p (gi);
+	       gsi_next (&gi))
+	    {
+	      gimple *gs = gsi_stmt (gi);
+	      if (!is_gimple_assign (gs))
+		{
+		  only_mask_ops = false;
+		  break;
+		}
+	      tree_code c = gimple_assign_rhs_code (gs);
+	      if (TREE_CODE_CLASS (c) == tcc_comparison)
+		has_cmp = true;
+	      else if (!CONVERT_EXPR_CODE_P (c) && c != NEGATE_EXPR)
+		{
+		  only_mask_ops = false;
+		  break;
+		}
+	    }
+	  if (only_mask_ops && has_cmp)
 	    check_it = true;
-        }
+	}
       if (!check_it)
 	;
       else if (contains_hot_cold_predict (middle_bb))
@@ -1128,6 +1170,20 @@ match_simplify_replacement (basic_block cond_bb, basic_block middle_bb,
 	       && middle_bb != middle_bb_alt
 	       && contains_hot_cold_predict (middle_bb_alt))
 	return false;
+      /* Materializing a comparison mask for a diamond that other paths
+	 enter (through its join or through a shared arm) would leave
+	 a PHI mixing the mask with the other paths' values: a residual
+	 half-diamond that neither ifcombine nor RTL if-conversion
+	 recovers, whereas they do handle the original nested branches
+	 (e.g. into ccmp chains).  Leave such embedded diamonds alone.  */
+      else if (result != cmp0 && result != cmp1
+	       && (EDGE_COUNT (gimple_bb (phi)->preds) != 2
+		   || (middle_bb != gimple_bb (phi)
+		       && !single_pred_p (middle_bb))
+		   || (threeway_p
+		       && middle_bb_alt != gimple_bb (phi)
+		       && !single_pred_p (middle_bb_alt))))
+	return false;
     }
 
   if (!result)
