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)