From: Kyrylo Tkachov <[email protected]>

The backward jump threader tail-duplicates an if-convertible "diamond" chain
when the same predicate controls several diamonds.  The astcenc "none" backend
lowers a per-lane "r = mask ? hi : lo" select, where one comparison mask drives
two selects, to such a chain:

    m = (a > b) ? -1 : 0;     // a comparison mask, kept as PHI<0, -1>
    ...
    if (m == -1) ...          // select 1 under the mask
    ...
    if (m == -1) ...          // select 2 under the same mask

The threader sees the later tests are redundant on the path through the first
(m, hence m == -1, is known) and tail-duplicates the intervening diamonds to
fold the branch away, turning an N-diamond chain into a 2^N decision tree that
reconverges at a single multi-predecessor merge.  phiopt and RTL if-conversion
can no longer recognise the per-select conditional moves, so they come out as
compare+branch.  On 731.astcenc_r (SPEC CPU2026, aarch64) this is the dominant
 gap in compute_quantized_weights_for_decimation, where -O3 emits
the hot select kernel with ~34 branches / 12 fcsel instead of ~5 / 24.
This pass gives ~19% improvement on that benchmark overall.

Rather than restrict the threader (which is doing a legitimate transform),
this removes the underlying redundancy with two value/control simplifications 
that improve the
GIMPLE in their own right; a beneficial side effect is that the threader no 
longer
finds correlated conditions to tail-duplicate.  They run in a small pass,
pass_merge_diamonds, placed before pass_thread_jumps_full and gated on
flag_thread_jumps, so it runs exactly when the backward threader does (under
-fno-thread-jumps the diamonds stay clean and if-conversion handles them):

  1. De-indirection: a test "m == C" / "m != C" whose operand m is a PHI of two
     integer constants -- a mask "cmp ? cst : cst" from a clean diamond -- is
     rewritten to test the mask's own controlling condition cmp.  We already
     perform this for a single-use mask; here it is exposed across a multi-use
     mask PHI.  The mask PHI then becomes dead.

  2. Merge: two if-convertible diamonds controlled by an identical condition,
     the first dominating the second, are merged -- the live PHIs of the first
     are recomputed in the second under its (identical) branch and the first
     branch is folded to a constant.  Folding the condition (rather than
     editing edges) leaves the CFG topology and dominator info valid for the
     rest of the pass; the requested cfg cleanup removes the dead arm.

The result is a chain of distinct-condition, two-PHI diamonds that the threader
leaves alone and RTL if-conversion lowers to conditional moves.  Whether each
select becomes a conditional move stays an RTL if-conversion cost-model
decision, so code that benefits from branches (e.g. predictable chess-eval
selects) is unaffected.

Bootstrapped and regtested on aarch64-unknown-linux-gnu.

Richard, is this the direction you had in mind for this PR?

Thanks,
Kyrill

Signed-off-by: Kyrylo Tkachov <[email protected]>

gcc/ChangeLog:

        PR tree-optimization/125672
        * tree-ssa-ifcombine.cc (ifcvt_diamond_join): New function.
        (deindirect_mask_cond): New function.
        (all_uses_dominated_by): New function.
        (merge_cond_diamond): New function.
        (class pass_merge_diamonds): New pass.
        (make_pass_merge_diamonds): New function.
        * tree-pass.h (make_pass_merge_diamonds): Declare.
        * passes.def: Run pass_merge_diamonds before pass_thread_jumps_full.

gcc/testsuite/ChangeLog:

        PR tree-optimization/125672
        * gcc.dg/tree-ssa/pr125672.c: New test.
        * gcc.dg/tree-ssa/pr125672-2.c: New test.
        * gcc.dg/tree-ssa/pr125672-3.c: New test.
---
 gcc/passes.def                             |   1 +
 gcc/testsuite/gcc.dg/tree-ssa/pr125672-2.c |  32 ++
 gcc/testsuite/gcc.dg/tree-ssa/pr125672-3.c |  15 +
 gcc/testsuite/gcc.dg/tree-ssa/pr125672.c   |  26 ++
 gcc/tree-pass.h                            |   1 +
 gcc/tree-ssa-ifcombine.cc                  | 381 +++++++++++++++++++++
 6 files changed, 456 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125672-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125672-3.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125672.c

diff --git a/gcc/passes.def b/gcc/passes.def
index 1fc867fae51..3c4f0629e78 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -233,6 +233,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_return_slot);
       NEXT_PASS (pass_fre, true /* may_iterate */);
       NEXT_PASS (pass_merge_phi);
+      NEXT_PASS (pass_merge_diamonds);
       NEXT_PASS (pass_thread_jumps_full, /*first=*/true);
       NEXT_PASS (pass_vrp, false /* final_p*/);
       NEXT_PASS (pass_array_bounds);
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125672-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-2.c
new file mode 100644
index 00000000000..59c7762a3b3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-2.c
@@ -0,0 +1,32 @@
+/* Execution test: merging same-condition if-convertible diamonds must
+   preserve semantics.  PR tree-optimization/125672.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+__attribute__((noipa)) static void
+kern (int n, const int *a, const int *b, int *o)
+{
+  for (int i = 0; i < n; i++)
+    {
+      int m0 = (a[i] > b[i]) ? -1 : 0;        /* a comparison mask */
+      int m1 = (a[i] < b[i]) ? -1 : 0;        /* a second mask */
+      o[4*i + 0] = (m0 == -1) ? a[i] : b[i];  /* select under mask 0 */
+      o[4*i + 1] = (m1 == -1) ? a[i] : b[i];  /* select under mask 1 */
+      o[4*i + 2] = (m0 == -1) ? b[i] : a[i];  /* mask 0 again */
+      o[4*i + 3] = (m1 == -1) ? b[i] : a[i];  /* mask 1 again */
+    }
+}
+
+int
+main (void)
+{
+  int a[3] = { 5, 2, 9 };
+  int b[3] = { 3, 7, 9 };
+  int o[12];
+  int exp[12] = { 5, 3, 3, 5,   7, 2, 2, 7,   9, 9, 9, 9 };
+  kern (3, a, b, o);
+  for (int i = 0; i < 12; i++)
+    if (o[i] != exp[i])
+      __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125672-3.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-3.c
new file mode 100644
index 00000000000..811b6490357
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-3.c
@@ -0,0 +1,15 @@
+/* A single comparison mask feeding selects is already simplified by phiopt,
+   so pass_merge_diamonds is not needed and must not fire here -- it only acts
+   when interleaved masks defeat phiopt.  PR tree-optimization/125672.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-mergediam-details" } */
+
+void
+f (float a, float b, int x, int y, int u, int v, int *o)
+{
+  int m = (a > b) ? -1 : 0;
+  o[0] = (m == -1) ? x : y;
+  o[1] = (m == -1) ? u : v;
+}
+
+/* { dg-final { scan-tree-dump-not "de-indirecting mask test" "mergediam" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125672.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125672.c
new file mode 100644
index 00000000000..87acf94a559
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125672.c
@@ -0,0 +1,26 @@
+/* PR tree-optimization/125672: when one comparison mask drives several
+   per-lane selects, the backward jump threader tail-duplicates the
+   if-convertible diamond chain.  pass_merge_diamonds removes the redundancy
+   before threading: it de-indirects the mask comparisons and merges the
+   diamonds controlled by an identical condition.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-mergediam-details" } */
+
+void
+f (float a, float b, float c, float d,
+   int x0, int y0, int x1, int y1,
+   int u0, int v0, int u1, int v1, int *o)
+{
+  int m0 = (a > b) ? -1 : 0;
+  int m1 = (c > d) ? -1 : 0;
+  o[0] = (m0 == -1) ? x0 : y0;
+  o[1] = (m1 == -1) ? x1 : y1;
+  o[2] = (m0 == -1) ? u0 : v0;
+  o[3] = (m1 == -1) ? u1 : v1;
+}
+
+/* The mask comparisons are de-indirected to test the comparison directly...  
*/
+/* { dg-final { scan-tree-dump "de-indirecting mask test" "mergediam" } } */
+/* ... and the two diamonds per mask are merged into one.  */
+/* { dg-final { scan-tree-dump "merging if-convertible diamond" "mergediam" } 
} */
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index b3c97658a8f..1625c1766e0 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -464,6 +464,7 @@ extern gimple_opt_pass *make_pass_phiopt (gcc::context 
*ctxt);
 extern gimple_opt_pass *make_pass_forwprop (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_phiprop (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tree_ifcombine (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_merge_diamonds (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_dse (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_nrv (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_rename_ssa_copies (gcc::context *ctxt);
diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index 6489abf3a06..96414131c36 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -1358,6 +1358,387 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
 
 /* Main entry for the tree if-conversion pass.  */
 
+/* Merge if-convertible "diamonds" controlled by an identical condition, and
+   de-indirect mask comparisons that feed them, before the backward jump
+   threaders run.
+
+   When a comparison mask drives several selects, as in
+
+       m = (a > b) ? -1 : 0; // comparison mask, kept as a PHI of 0 and -1
+       if (m == -1) ... // a select under the mask
+       if (m == -1) ... // another select under the same mask
+
+   the per-lane selects lower to a chain of if-convertible diamonds in which
+   the same predicate controls several diamonds.  The backward jump threader
+   then notices the later tests are redundant on the path through the earlier
+   one (m, and hence "m == -1", is known there) and tail-duplicates the
+   intervening diamonds to fold the branch away, turning an N-diamond chain
+   into a 2^N decision tree that reconverges at a single multi-predecessor
+   merge.  RTL if-conversion can no longer recover the per-select conditional
+   moves and emits compare+branch instead.
+
+   This pass removes the redundancy at its two sources, so there is nothing for
+   threading to duplicate.
+
+   De-indirection: a test "m == C" / "m != C" whose operand m is a PHI of two
+   integer constants (a mask "cmp ? cst : cst") is rewritten to test the mask's
+   own controlling condition cmp; the mask PHI then becomes dead.
+
+   Merge: two if-convertible diamonds controlled by an identical condition,
+   where the first dominates the second, are merged: the PHIs of the first are
+   recomputed in the second under its identical branch and the first branch is
+   folded away, so both selects live under one branch.
+
+   The result is a chain of distinct-condition diamonds that the threader
+   leaves alone and RTL if-conversion lowers to conditional moves; whether each
+   select actually becomes a conditional move stays an RTL if-conversion
+   cost-model decision.  */
+
+/* If COND_BB heads an if-then-else whose two arms are empty (no side effects)
+   and reconverge at a single join block, return that join block and set
+   *E_TRUE and *E_FALSE to the edges entering the join on the condition's true
+   and false side (possibly through one empty forwarder).  Otherwise NULL.  */
+
+static basic_block
+ifcvt_diamond_join (basic_block cond_bb, edge *e_true, edge *e_false)
+{
+  basic_block tb = NULL, fb = NULL;
+  if (!recognize_if_then_else (cond_bb, &tb, &fb))
+    return NULL;
+  edge te = find_edge (cond_bb, tb);
+  edge fe = find_edge (cond_bb, fb);
+
+  edge tj_e = te, fj_e = fe;
+  basic_block tj = tb, fj = fb;
+  if (single_pred_p (tb) && single_succ_p (tb) && empty_block_p (tb))
+    {
+      tj = single_succ (tb);
+      tj_e = single_succ_edge (tb);
+    }
+  if (single_pred_p (fb) && single_succ_p (fb) && empty_block_p (fb))
+    {
+      fj = single_succ (fb);
+      fj_e = single_succ_edge (fb);
+    }
+
+  /* One arm may reach the join directly (a triangle), with the other through
+     an empty forwarder, or both arms may forward to the join.  */
+  basic_block join;
+  if (tb == fj && tb != cond_bb)
+    {
+      join = tb;
+      *e_true = te;
+      *e_false = fj_e;
+    }
+  else if (fb == tj && fb != cond_bb)
+    {
+      join = fb;
+      *e_true = tj_e;
+      *e_false = fe;
+    }
+  else if (tj == fj && tj != cond_bb && tb != fb)
+    {
+      join = tj;
+      *e_true = tj_e;
+      *e_false = fj_e;
+    }
+  else
+    return NULL;
+
+  if (EDGE_COUNT (join->preds) != 2
+      || (*e_true)->src == (*e_false)->src)
+    return NULL;
+  if (tb != join && !bb_no_side_effects_p (tb))
+    return NULL;
+  if (fb != join && !bb_no_side_effects_p (fb))
+    return NULL;
+  return join;
+}
+
+/* Rewrite the GIMPLE_COND ending COND_BB if it tests "m == C" / "m != C"
+   where m is a PHI of two integer constants controlled by a clean diamond:
+   replace the test with that diamond's controlling condition.  This removes
+   the mask indirection so the predicate is exposed directly.  Returns true if
+   the condition was rewritten.  Requires valid dominator info; makes no CFG
+   change.  */
+
+static bool
+deindirect_mask_cond (basic_block cond_bb)
+{
+  gcond *gc = safe_dyn_cast <gcond *> (*gsi_last_bb (cond_bb));
+  if (!gc)
+    return false;
+  enum tree_code cc = gimple_cond_code (gc);
+  if (cc != EQ_EXPR && cc != NE_EXPR)
+    return false;
+
+  tree m = gimple_cond_lhs (gc);
+  tree c = gimple_cond_rhs (gc);
+  if (TREE_CODE (m) != SSA_NAME && TREE_CODE (c) == SSA_NAME)
+    std::swap (m, c);
+  if (TREE_CODE (m) != SSA_NAME || TREE_CODE (c) != INTEGER_CST)
+    return false;
+
+  gphi *mphi = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (m));
+  if (!mphi || gimple_phi_num_args (mphi) != 2)
+    return false;
+  basic_block jm = gimple_bb (mphi);
+
+  basic_block cm = get_immediate_dominator (CDI_DOMINATORS, jm);
+  if (!cm)
+    return false;
+  edge et, ef;
+  if (ifcvt_diamond_join (cm, &et, &ef) != jm)
+    return false;
+
+  tree vt = PHI_ARG_DEF_FROM_EDGE (mphi, et);
+  tree vf = PHI_ARG_DEF_FROM_EDGE (mphi, ef);
+  if (TREE_CODE (vt) != INTEGER_CST || TREE_CODE (vf) != INTEGER_CST)
+    return false;
+
+  bool c_eq_vt = operand_equal_p (vt, c, 0);
+  bool c_eq_vf = operand_equal_p (vf, c, 0);
+  /* Whether "m == c" is true when cm's branch is taken.  */
+  bool when_true;
+  if (c_eq_vt && !c_eq_vf)
+    when_true = true;
+  else if (c_eq_vf && !c_eq_vt)
+    when_true = false;
+  else
+    return false;
+  if (cc == NE_EXPR)
+    when_true = !when_true;
+
+  gcond *ctrl = as_a <gcond *> (*gsi_last_bb (cm));
+  enum tree_code ctrl_code = gimple_cond_code (ctrl);
+  tree ctrl_lhs = gimple_cond_lhs (ctrl);
+  tree ctrl_rhs = gimple_cond_rhs (ctrl);
+  if (!when_true)
+    {
+      ctrl_code = invert_tree_comparison (ctrl_code, HONOR_NANS (ctrl_lhs));
+      if (ctrl_code == ERROR_MARK)
+       return false;
+    }
+
+  gimple_cond_set_code (gc, ctrl_code);
+  gimple_cond_set_lhs (gc, ctrl_lhs);
+  gimple_cond_set_rhs (gc, ctrl_rhs);
+  update_stmt (gc);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+            "de-indirecting mask test in bb%d via diamond bb%d\n",
+            cond_bb->index, cm->index);
+  return true;
+}
+
+/* Return true if every use of NAME is dominated by block BY (treating a use
+   in a PHI as occurring on the corresponding predecessor edge).  */
+
+static bool
+all_uses_dominated_by (tree name, basic_block by)
+{
+  imm_use_iterator it;
+  use_operand_p use_p;
+  gimple *use_stmt;
+  FOR_EACH_IMM_USE_FAST (use_p, it, name)
+    {
+      use_stmt = USE_STMT (use_p);
+      if (is_gimple_debug (use_stmt))
+       continue;
+      basic_block ub;
+      if (gphi *phi = dyn_cast <gphi *> (use_stmt))
+       ub = gimple_phi_arg_edge (phi, PHI_ARG_INDEX_FROM_USE (use_p))->src;
+      else
+       ub = gimple_bb (use_stmt);
+      if (!dominated_by_p (CDI_DOMINATORS, ub, by))
+       return false;
+    }
+  return true;
+}
+
+/* Try to merge the same-condition diamond headed by B2 into a dominating
+   diamond.  Return true if a merge was performed.  */
+
+static bool
+merge_cond_diamond (basic_block b2)
+{
+  edge t2, f2;
+  basic_block join2 = ifcvt_diamond_join (b2, &t2, &f2);
+  if (!join2)
+    return false;
+  gcond *c2 = as_a <gcond *> (*gsi_last_bb (b2));
+
+  for (basic_block b1 = get_immediate_dominator (CDI_DOMINATORS, b2);
+       b1; b1 = get_immediate_dominator (CDI_DOMINATORS, b1))
+    {
+      gcond *c1 = safe_dyn_cast <gcond *> (*gsi_last_bb (b1));
+      if (!c1)
+       continue;
+      if (gimple_cond_code (c1) != gimple_cond_code (c2)
+         || !operand_equal_p (gimple_cond_lhs (c1), gimple_cond_lhs (c2), 0)
+         || !operand_equal_p (gimple_cond_rhs (c1), gimple_cond_rhs (c2), 0))
+       continue;
+
+      edge t1, f1;
+      basic_block join1 = ifcvt_diamond_join (b1, &t1, &f1);
+      if (!join1 || join1 == join2 || join1 == b2)
+       continue;
+
+      /* Every live non-virtual PHI of JOIN1 must be recomputable at JOIN2;
+        JOIN1 must carry no virtual PHI.  */
+      bool ok = true, any_phi = false;
+      for (gphi_iterator gpi = gsi_start_phis (join1);
+          !gsi_end_p (gpi); gsi_next (&gpi))
+       {
+         gphi *phi = gpi.phi ();
+         tree res = gimple_phi_result (phi);
+         if (virtual_operand_p (res))
+           {
+             ok = false;
+             break;
+           }
+         any_phi = true;
+         if (!has_zero_uses (res) && !all_uses_dominated_by (res, join2))
+           {
+             ok = false;
+             break;
+           }
+       }
+      if (!ok || !any_phi)
+       continue;
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       fprintf (dump_file,
+                "merging if-convertible diamond bb%d into same-condition "
+                "diamond bb%d\n", b1->index, b2->index);
+
+      /* Move each live PHI of JOIN1 to JOIN2 under B2's branch; drop dead
+        ones.  */
+      for (gphi_iterator gpi = gsi_start_phis (join1); !gsi_end_p (gpi);)
+       {
+         gphi *phi = gpi.phi ();
+         tree res = gimple_phi_result (phi);
+         if (!has_zero_uses (res))
+           {
+             tree tv = PHI_ARG_DEF_FROM_EDGE (phi, t1);
+             tree fv = PHI_ARG_DEF_FROM_EDGE (phi, f1);
+             location_t tl = gimple_phi_arg_location_from_edge (phi, t1);
+             location_t fl = gimple_phi_arg_location_from_edge (phi, f1);
+             tree nres = copy_ssa_name (res);
+             gphi *nphi = create_phi_node (nres, join2);
+             add_phi_arg (nphi, tv, t2, tl);
+             add_phi_arg (nphi, fv, f2, fl);
+
+             imm_use_iterator it;
+             use_operand_p use_p;
+             gimple *use_stmt;
+             FOR_EACH_IMM_USE_STMT (use_stmt, it, res)
+               {
+                 /* Non-debug uses are all dominated by JOIN2 (checked above).
+                    A debug use may sit in a block JOIN2 does not dominate; the
+                    recomputed value is not available there, so reset it rather
+                    than create invalid SSA.  */
+                 if (is_gimple_debug (use_stmt)
+                     && !dominated_by_p (CDI_DOMINATORS,
+                                         gimple_bb (use_stmt), join2))
+                   {
+                     gimple_debug_bind_reset_value (use_stmt);
+                     update_stmt (use_stmt);
+                     continue;
+                   }
+                 FOR_EACH_IMM_USE_ON_STMT (use_p, it)
+                   SET_USE (use_p, nres);
+                 update_stmt (use_stmt);
+               }
+           }
+         remove_phi_node (&gpi, true);
+       }
+
+      /* Fold away B1's now-redundant branch.  Both arms reach JOIN1, which no
+        longer holds a PHI distinguishing them, so the branch is dead.  Making
+        the condition constant (rather than editing edges) forces it to the
+        true edge, which reaches JOIN1 directly or via the empty arm, and keeps
+        the CFG and dominator info valid for the rest of the pass; cfg cleanup
+        then removes the dead edge and empty block.  */
+      gimple_cond_make_true (c1);
+      update_stmt (c1);
+
+      return true;
+    }
+  return false;
+}
+
+namespace {
+
+const pass_data pass_data_merge_diamonds =
+{
+  GIMPLE_PASS, /* type */
+  "mergediam", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_IFCOMBINE, /* tv_id */
+  PROP_cfg | PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_merge_diamonds : public gimple_opt_pass
+{
+public:
+  pass_merge_diamonds (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_merge_diamonds, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  /* This pass only removes redundancy that the backward jump threader would
+     otherwise tail-duplicate, so it is only useful when that threader runs.  
*/
+  bool gate (function *) final override { return flag_thread_jumps; }
+  unsigned int execute (function *) final override;
+
+}; // class pass_merge_diamonds
+
+unsigned int
+pass_merge_diamonds::execute (function *fun)
+{
+  bool any = false;
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  /* Phase 1: de-indirect mask comparisons (no CFG change).  */
+  bool di = true;
+  while (di)
+    {
+      di = false;
+      basic_block bb;
+      FOR_EACH_BB_FN (bb, fun)
+       if (deindirect_mask_cond (bb))
+         di = any = true;
+    }
+
+  /* Phase 2: merge diamonds controlled by identical conditions.  Neither the
+     PHI motion nor folding a branch to a constant changes the CFG topology, so
+     dominator info stays valid throughout: a single walk suffices, and the
+     inner loop drains a whole chain of same-condition diamonds into BB.  A
+     folded branch becomes constant, so merge_cond_diamond never re-matches it,
+     and the walk need not be in dominator order. A chain collapses into its
+     deepest member whichever way it is visited.  */
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, fun)
+    while (merge_cond_diamond (bb))
+      any = true;
+  return any ? TODO_cleanup_cfg : 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_merge_diamonds (gcc::context *ctxt)
+{
+  return new pass_merge_diamonds (ctxt);
+}
+
+
 namespace {
 
 const pass_data pass_data_tree_ifcombine =
-- 
2.50.1 (Apple Git-155)

Reply via email to