Hi Richard, New version in https://gcc.gnu.org/pipermail/gcc-patches/2026-June/721148.html.
On Tue, May 5, 2026 at 11:35 AM Richard Biener <[email protected]> wrote: > On Wed, Mar 18, 2026 at 11:11 AM Konstantinos Eleftheriou > <[email protected]> wrote: > > > > Please ignore the change for "gcc.dg/guality/pr54693-2.c". It turns out > that the tweak is not needed with the updated > > solution and we will revert it if this gets accepted. > > > > Thanks, > > Konstantinos > > > > On Tue, Mar 17, 2026 at 2:23 PM Konstantinos Eleftheriou < > [email protected]> wrote: > >> > >> After tail merging combines duplicate blocks, their predecessors branch > >> to the same successor. On targets that support conditional compares > >> (ccmp), combine the sequential conditions leading to the merged block > >> using the ifcombine infrastructure. This enables the generation of ccmp > >> instructions. > >> > >> The candidate selection identifies predecessor blocks of the merged > block > >> that have conditional branches, excluding the immediate dominator. > >> After the tail-merge loop completes, dominance info is (re)computed, SSA > >> names that may be undefined are marked, and tree_ssa_ifcombine_bb is > >> called for each candidate. > >> > >> To avoid bogus -Wmaybe-uninitialized warnings, candidates are skipped > >> when the dominance subtree of a successor has PHI arguments that are > >> maybe-undef at the dominance frontier, as combining conditions in that > >> case would change the predicate structure the uninit analysis relies on. > >> > >> The xfail condition for gcc.dg/guality/pr54693-2.c is adjusted because > >> the newly combined conditions change the debug info on AArch64. > >> > >> gcc/ChangeLog: > >> > >> PR tree-optimization/102793 > >> > >> * tree-ssa-ifcombine.h: Remove extra blank line at end. > >> * tree-ssa-tail-merge.cc: Include target.h, > tree-ssa-ifcombine.h, > >> and tree-ssa.h. > >> (ifcombine_candidate_bbs): New static bitmap. > >> (apply_clusters): After replacing a block, identify predecessors > >> of the merged block as ifcombine candidates. > >> (maybe_undef_at_dom_frontier_p): New function. > >> (tail_merge_optimize): Compute dominance info when needed for > >> ifcombine. Call mark_ssa_maybe_undefs and tree_ssa_ifcombine_bb > >> for each candidate on ccmp targets, skipping candidates with > >> maybe-undef PHI args at the dominance frontier. Return > >> TODO_cleanup_cfg when ifcombine changed the CFG. > >> > >> gcc/testsuite/ChangeLog: > >> > >> PR tree-optimization/102793 > >> > >> * gcc.dg/guality/pr54693-2.c: Adjust xfail condition for > >> AArch64 to account for the new ccmp-related transformations. > >> * gcc.dg/tree-ssa/pr102793-1.c: New test. > >> * gcc.dg/tree-ssa/pr102793-2.c: New test. > >> > >> Signed-off-by: Konstantinos Eleftheriou < > [email protected]> > >> --- > >> > >> Changes in v2: > >> - Use tree_ssa_ifcombine_bb for combining conditions. > >> > >> gcc/testsuite/gcc.dg/guality/pr54693-2.c | 2 +- > >> gcc/testsuite/gcc.dg/tree-ssa/pr102793-1.c | 50 ++++++++ > >> gcc/testsuite/gcc.dg/tree-ssa/pr102793-2.c | 51 +++++++++ > >> gcc/tree-ssa-ifcombine.h | 1 - > >> gcc/tree-ssa-tail-merge.cc | 126 ++++++++++++++++++++- > >> 5 files changed, 225 insertions(+), 5 deletions(-) > >> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr102793-1.c > >> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr102793-2.c > >> > >> diff --git a/gcc/testsuite/gcc.dg/guality/pr54693-2.c > b/gcc/testsuite/gcc.dg/guality/pr54693-2.c > >> index 229ef0efbea0..9d0080782410 100644 > >> --- a/gcc/testsuite/gcc.dg/guality/pr54693-2.c > >> +++ b/gcc/testsuite/gcc.dg/guality/pr54693-2.c > >> @@ -18,7 +18,7 @@ foo (int x, int y, int z) > >> while (x > 3 && y > 3 && z > 3) > >> { /* { dg-final { gdb-test .+2 "i" "v + 1" } } */ > >> /* { dg-final { gdb-test .+1 "x" "10 - i" { xfail { > aarch64*-*-* && { any-opts "-fno-fat-lto-objects" } } } } } */ > >> - bar (i); /* { dg-final { gdb-test . "y" "20 - 2 * i" { xfail { > aarch64*-*-* && { any-opts "-fno-fat-lto-objects" "-Os" } } } } } */ > >> + bar (i); /* { dg-final { gdb-test . "y" "20 - 2 * i" { xfail { > aarch64*-*-* && { { any-opts "-flto" } && { no-opts > "-fno-use-linker-plugin" } } } } } } */ > >> /* { dg-final { gdb-test .-1 "z" "30 - 3 * i" { xfail { > aarch64*-*-* && { any-opts "-fno-fat-lto-objects" "-Os" } } } } } */ > >> i++, x--, y -= 2, z -= 3; > >> } > >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr102793-1.c > b/gcc/testsuite/gcc.dg/tree-ssa/pr102793-1.c > >> new file mode 100644 > >> index 000000000000..074abdf4936b > >> --- /dev/null > >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr102793-1.c > >> @@ -0,0 +1,50 @@ > >> +/* { dg-do compile } */ > >> +/* { dg-skip-if "requires ccmp support" { ! { aarch64*-*-* || apxf } } > } */ > >> +/* { dg-options "-O3 -fdump-tree-pre" } */ > >> + > >> +typedef unsigned long uint64_t; > >> + > >> +int foo(void); > >> + > >> +int ccmp(uint64_t* s1, uint64_t* s2) > >> +{ > >> + uint64_t d1, d2, bar; > >> + d1 = *s1++; > >> + d2 = *s2++; > >> + bar = (d1 ^ d2) & 0xabcd; > >> + if (bar == 0 || d1 != d2) > >> + return foo(); > >> + return 0; > >> +} > >> + > >> +int noccmp0(uint64_t* s1, uint64_t* s2) > >> +{ > >> + uint64_t d1, d2, bar; > >> + > >> + d1 = *s1++; > >> + d2 = *s2++; > >> + bar = (d1 ^ d2) & 0xabcd; > >> + if (bar == 0) > >> + return foo(); > >> + if (d1 != d2) > >> + return foo(); > >> + return 0; > >> +} > >> + > >> +int noccmp1(uint64_t* s1, uint64_t* s2) > >> +{ > >> + uint64_t d1, d2, d3, d4, bar; > >> + d1 = *s1++; > >> + d2 = *s2++; > >> + d3 = *s1++; > >> + d4 = *s2++; > >> + bar = (d1 ^ d2) & 0xabcd; > >> + if (bar == 0) > >> + return foo(); > >> + if (d3 != d4) > >> + return foo(); > >> + return 0; > >> +} > >> + > >> +/* Check for condition assignments for noccmp0 and noccmp1. */ > >> +/* { dg-final { scan-tree-dump-times {_\d+ = d\d+_\d+ != d\d+_\d+;\n > _\d+ = bar_\d+ == 0;} 2 "pre" } } */ > >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr102793-2.c > b/gcc/testsuite/gcc.dg/tree-ssa/pr102793-2.c > >> new file mode 100644 > >> index 000000000000..99eb52d35f54 > >> --- /dev/null > >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr102793-2.c > >> @@ -0,0 +1,51 @@ > >> +/* { dg-do compile } */ > >> +/* { dg-skip-if "requires ccmp support" { ! { aarch64*-*-* || apxf } } > } */ > >> +/* { dg-options "-O3 -fdump-tree-pre" } */ > >> + > >> +typedef unsigned long uint64_t; > >> + > >> +int foo(void); > >> + > >> +uint64_t noccmp0(uint64_t* s1, uint64_t* s2) > >> +{ > >> + uint64_t d1, d2, d3, d4, bar; > >> + d1 = *s1++; > >> + d2 = *s2++; > >> + d3 = *s1++; > >> + d4 = *s2++; > >> + bar = (d1 ^ d2) & 0xabcd; > >> + if (bar == 0) > >> + return foo(); > >> + if (d3 != d4) > >> + d3++; > >> + else > >> + return foo(); > >> + return d3; > >> +} > >> + > >> +uint64_t noccmp1(uint64_t* s1, uint64_t* s2) > >> +{ > >> + uint64_t d1, d2, d3, d4, bar; > >> + d1 = *s1++; > >> + d2 = *s2++; > >> + d3 = *s1++; > >> + d4 = *s2++; > >> + bar = (d1 ^ d2) & 0xabcd; > >> + if (bar == 0) > >> + d3++; > >> + else > >> + return foo(); > >> + if (d3 > d4) > >> + d3++; > >> + else if (d1 != d2) > >> + return foo (); > >> + d3 = d3 + d4 + 1; > >> + return d3; > >> +} > >> + > >> +/* Check for condition assignments in the case that the transformation > >> + is applied. > >> + The transformation should not be applied on noccmp1, where the foo > call is > >> + on the false branch of the first condition. */ > >> +/* { dg-final { scan-tree-dump-times {_\d+ = d\d+_\d+ != d\d+_\d+;\n > _\d+ = bar_\d+ != 0;} 1 "pre" } } */ > >> +/* { dg-final { scan-tree-dump-times {if \(bar_\d+ == 0\)} 1 "pre" } } > */ > >> diff --git a/gcc/tree-ssa-ifcombine.h b/gcc/tree-ssa-ifcombine.h > >> index fc1e3a100cd1..95daac2d942a 100644 > >> --- a/gcc/tree-ssa-ifcombine.h > >> +++ b/gcc/tree-ssa-ifcombine.h > >> @@ -26,4 +26,3 @@ bool tree_ssa_ifcombine_bb (basic_block); > >> > >> #endif > >> > >> - > >> diff --git a/gcc/tree-ssa-tail-merge.cc b/gcc/tree-ssa-tail-merge.cc > >> index 4b9dbd886b6a..f72b30134c54 100644 > >> --- a/gcc/tree-ssa-tail-merge.cc > >> +++ b/gcc/tree-ssa-tail-merge.cc > >> @@ -189,6 +189,7 @@ along with GCC; see the file COPYING3. If not see > >> #include "system.h" > >> #include "coretypes.h" > >> #include "backend.h" > >> +#include "target.h" > >> #include "tree.h" > >> #include "gimple.h" > >> #include "cfghooks.h" > >> @@ -202,9 +203,11 @@ along with GCC; see the file COPYING3. If not see > >> #include "tree-cfg.h" > >> #include "tree-into-ssa.h" > >> #include "tree-ssa-sccvn.h" > >> +#include "tree-ssa-ifcombine.h" > >> #include "cfgloop.h" > >> #include "tree-eh.h" > >> #include "tree-cfgcleanup.h" > >> +#include "tree-ssa.h" > >> > >> const int ignore_edge_flags = EDGE_DFS_BACK | EDGE_EXECUTABLE; > >> > >> @@ -1707,6 +1710,8 @@ replace_block_by (basic_block bb1, basic_block > bb2) > >> > >> static bitmap update_bbs; > >> > >> +static bitmap ifcombine_candidate_bbs; > >> + > >> /* For each cluster in all_clusters, merge all cluster->bbs. Returns > >> number of bbs removed. */ > >> > >> @@ -1735,6 +1740,27 @@ apply_clusters (void) > >> bitmap_clear_bit (update_bbs, bb1->index); > >> > >> replace_block_by (bb1, bb2); > >> + > >> + basic_block imm_dominator = get_immediate_dominator ( > >> + CDI_DOMINATORS, bb2); > >> + > >> + /* Find conditions in if-statements that lead to bb. */ > >> + edge e; > >> + edge_iterator ei; > >> + FOR_EACH_EDGE (e, ei, bb2->preds) > >> + { > >> + basic_block then_tmp = NULL; > >> + basic_block else_tmp = NULL; > >> + if (recognize_if_then_else (e->src, &bb2, &else_tmp) > >> + || recognize_if_then_else (e->src, &then_tmp, &bb2)) > >> + { > >> + gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb > (e->src)); > >> + if (cond) > >> + if (e->src != imm_dominator) > > this looks like a condition that can be checked even before calling > recognize_if_then_else. I'd also say you can assert there's a gcond > if recognize_if_then_else was successful. > Fixed. > > >> + bitmap_set_bit (ifcombine_candidate_bbs, > e->src->index); > >> + } > >> + } > >> + > >> nr_bbs_removed++; > >> } > >> } > >> @@ -1797,6 +1823,55 @@ update_debug_stmts (void) > >> } > >> } > >> > >> +/* Return true if the dominance subtree rooted at BB has any outgoing > edge > >> + to a block outside the subtree where the PHI at the target has a > >> + maybe-undef argument on that edge. */ > >> + > >> +static bool > >> +maybe_undef_at_dom_frontier_p (basic_block bb) > >> +{ > >> + auto_vec<basic_block, 32> dom_bbs; > >> + auto_bitmap dominated; > >> + > >> + /* Collect all blocks dominated by BB. */ > >> + dom_bbs.safe_push (bb); > >> + bitmap_set_bit (dominated, bb->index); > >> + for (unsigned int wi = 0; wi < dom_bbs.length (); wi++) > >> + { > >> + basic_block b = dom_bbs[wi]; > >> + for (basic_block son = first_dom_son (CDI_DOMINATORS, b); > >> + son; son = next_dom_son (CDI_DOMINATORS, son)) > >> + { > >> + bitmap_set_bit (dominated, son->index); > >> + dom_bbs.safe_push (son); > >> + } > >> + } > > get_all_dominated_blocks? > I'll note this is now O (number-of-ifcombine-candidates * function-size), > aka > quadratic. > > IMO we shouldn't work around uninit diagnostic deficiencies here but try to > deal with those in uninit. > We dropped the workaround and we now address this in uninit. > > Otherwise looks OK to me. I do wonder whether we should really > conditionalize this on ccmp availability though. ifcombine itself > doesn't, and if it were to run after tail-merging we'd merge those > compares. > We dropped the ccmp availability gate and we now rely on the cost-model to do the right thing. Thanks, Konstantinos. > > Richard. > > >> + > >> + /* Check edges from dominated blocks to non-dominated blocks > >> + for maybe-undef PHI arguments. */ > >> + for (auto b : dom_bbs) > >> + { > >> + edge e; > >> + edge_iterator ei; > >> + FOR_EACH_EDGE (e, ei, b->succs) > >> + { > >> + if (bitmap_bit_p (dominated, e->dest->index)) > >> + continue; > >> + for (gphi_iterator gsi = gsi_start_phis (e->dest); > >> + !gsi_end_p (gsi); gsi_next (&gsi)) > >> + { > >> + gphi *phi = gsi.phi (); > >> + tree arg = PHI_ARG_DEF_FROM_EDGE (phi, e); > >> + if (TREE_CODE (arg) == SSA_NAME > >> + && ssa_name_maybe_undef_p (arg)) > >> + return true; > >> + } > >> + } > >> + } > >> + > >> + return false; > >> +} > >> + > >> /* Runs tail merge optimization. */ > >> > >> unsigned int > >> @@ -1807,6 +1882,7 @@ tail_merge_optimize (bool need_crit_edge_split) > >> bool loop_entered = false; > >> int iteration_nr = 0; > >> int max_iterations = param_max_tail_merge_iterations; > >> + unsigned int todo = 0; > >> > >> if (!flag_tree_tail_merge > >> || max_iterations == 0) > >> @@ -1833,6 +1909,7 @@ tail_merge_optimize (bool need_crit_edge_split) > >> loop_entered = true; > >> alloc_cluster_vectors (); > >> update_bbs = BITMAP_ALLOC (NULL); > >> + ifcombine_candidate_bbs = BITMAP_ALLOC (NULL); > >> } > >> else > >> reset_cluster_vectors (); > >> @@ -1866,10 +1943,50 @@ tail_merge_optimize (bool need_crit_edge_split) > >> > >> if (nr_bbs_removed_total > 0) > >> { > >> + bool need_dominance > >> + = MAY_HAVE_DEBUG_BIND_STMTS > >> + || (targetm.have_ccmp () > >> + && !bitmap_empty_p (ifcombine_candidate_bbs)); > >> + > >> + if (need_dominance) > >> + calculate_dominance_info (CDI_DOMINATORS); > >> + > >> if (MAY_HAVE_DEBUG_BIND_STMTS) > >> + update_debug_stmts (); > >> + > >> + unsigned int i; > >> + bitmap_iterator bi; > >> + bool cfg_changed = false; > >> + /* On targets that support conditional compares (ccmp), try to > combine > >> + conditions of blocks that were made to branch to the same > successor > >> + by tail merging. This is gated on ccmp support because it will > >> + produce beneficial code when ccmp instructions are available. > */ > >> + if (targetm.have_ccmp () > >> + && !bitmap_empty_p (ifcombine_candidate_bbs)) > >> { > >> - calculate_dominance_info (CDI_DOMINATORS); > >> - update_debug_stmts (); > >> + mark_ssa_maybe_undefs (); > >> + EXECUTE_IF_SET_IN_BITMAP (ifcombine_candidate_bbs, 0, i, bi) > >> + { > >> + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, i); > >> + if (!bb) > >> + continue; > >> + > >> + /* Do not combine conditions when the common successor's > >> + dominance subtree has PHI arguments that are > maybe-undef. */ > >> + edge e; > >> + edge_iterator ei; > >> + bool dominated_undef = false; > >> + FOR_EACH_EDGE (e, ei, bb->succs) > >> + if (maybe_undef_at_dom_frontier_p (e->dest)) > >> + { > >> + dominated_undef = true; > >> + break; > >> + } > >> + if (dominated_undef) > >> + continue; > >> + > >> + cfg_changed |= tree_ssa_ifcombine_bb (bb); > >> + } > >> } > >> > >> if (dump_file && (dump_flags & TDF_DETAILS)) > >> @@ -1879,6 +1996,8 @@ tail_merge_optimize (bool need_crit_edge_split) > >> } > >> > >> mark_virtual_operands_for_renaming (cfun); > >> + > >> + todo |= cfg_changed ? TODO_cleanup_cfg : 0; > >> } > >> > >> delete_worklist (); > >> @@ -1886,9 +2005,10 @@ tail_merge_optimize (bool need_crit_edge_split) > >> { > >> delete_cluster_vectors (); > >> BITMAP_FREE (update_bbs); > >> + BITMAP_FREE (ifcombine_candidate_bbs); > >> } > >> > >> timevar_pop (TV_TREE_TAIL_MERGE); > >> > >> - return 0; > >> + return todo; > >> } > >> -- > >> 2.52.0 > >> >
