diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-17.c b/gcc/testsuite/gcc.dg/loop-unswitch-17.c index 8655e09a51c..4b806c475b1 100644 --- a/gcc/testsuite/gcc.dg/loop-unswitch-17.c +++ b/gcc/testsuite/gcc.dg/loop-unswitch-17.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-optimized" } */ +/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-optimized -fno-tree-ch" } */ int foo (int a) { diff --git a/gcc/testsuite/gcc.dg/pr103079.c b/gcc/testsuite/gcc.dg/pr103079.c index 7f6632fc669..7b107544725 100644 --- a/gcc/testsuite/gcc.dg/pr103079.c +++ b/gcc/testsuite/gcc.dg/pr103079.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-Os -fdump-tree-vrp2" } */ +/* { dg-options "-Os -fdump-tree-vrp2 -fno-tree-ch" } */ int a, b = -2; int main() { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-7.c b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-7.c index e2a6c75f2e9..b3df3b6398e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-7.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-7.c @@ -4,7 +4,7 @@ int is_sorted(int *a, int n, int m, int k) { if (k > 0) - for (int i = 0; i < n - 1 && m && k > i; i++) + for (int i = 0; k > i && m && i < n - 1 ; i++) if (a[i] > a[i + 1]) return 0; return 1; @@ -17,5 +17,4 @@ int is_sorted(int *a, int n, int m, int k) /* { dg-final { scan-tree-dump-times "Conditional combines static and invariant" 0 "ch2" } } */ /* { dg-final { scan-tree-dump-times "Will elliminate invariant exit" 1 "ch2" } } */ /* { dg-final { scan-tree-dump-times "Will eliminate peeled conditional" 1 "ch2" } } */ -/* { dg-final { scan-tree-dump-times "Not duplicating bb .: condition based on non-IV loop variant." 1 "ch2" } } */ /* { dg-final { scan-tree-dump-times "Will duplicate bb" 3 "ch2" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-9.c b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-9.c new file mode 100644 index 00000000000..7cc162ca94d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-9.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-ch-details" } */ +int a[100]; +void test (int m, int n) +{ + int i = 0; + do + { + if (m) + break; + i++; + a[i]=0; + } + while (i<10); +} +/* { dg-final { scan-tree-dump-times "Duplicating bb . is a win" 1 "ch2" } } */ +/* { dg-final { scan-tree-dump-times "May duplicate bb" 1 "ch2" } } */ +/* { dg-final { scan-tree-dump-times "Duplicating additional BB to obtain do-while loop" 1 "ch2" } } */ +/* { dg-final { scan-tree-dump-times "Will duplicate bb" 2 "ch2" } } */ +/* { dg-final { scan-tree-dump "is now do-while loop" "ch2" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-headers.c b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers.c index a5a82121ff2..f4f2b2aa70b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/copy-headers.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers.c @@ -12,4 +12,4 @@ void bla (void) } /* There should be a header duplicated. */ -/* { dg-final { scan-tree-dump-times "Duplicating header" 1 "ch2"} } */ +/* { dg-final { scan-tree-dump-times "Duplicating header of the" 1 "ch2"} } */ diff --git a/gcc/tree-ssa-loop-ch.cc b/gcc/tree-ssa-loop-ch.cc index a87ebc58e3d..6cdb87a762f 100644 --- a/gcc/tree-ssa-loop-ch.cc +++ b/gcc/tree-ssa-loop-ch.cc @@ -168,11 +168,28 @@ loop_combined_static_and_iv_p (class loop *loop, return gimple_uid (SSA_NAME_DEF_STMT (op)) & 4; } +/* Decision about posibility of copying a given header. */ + +enum ch_decision +{ + /* We do not want to copy this header. */ + ch_impossible, + /* We can copy it if it enables wins. */ + ch_possible, + /* We can "cop" it if it enables wins and doing + so will introduce no new code. */ + ch_possible_zero_cost, + /* We want to copy. */ + ch_win, + /* We want to copy and we will eliminate loop exit. */ + ch_win_invariant_exit +}; + /* Check whether we should duplicate HEADER of LOOP. At most *LIMIT instructions should be duplicated, limit is decreased by the actual amount. */ -static bool +static ch_decision should_duplicate_loop_header_p (basic_block header, class loop *loop, gimple_ranger *ranger, int *limit, @@ -190,7 +207,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, fprintf (dump_file, " Not duplicating bb %i: it is single succ.\n", header->index); - return false; + return ch_impossible; } if (flow_bb_inside_loop_p (loop, EDGE_SUCC (header, 0)->dest) @@ -200,7 +217,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, fprintf (dump_file, " Not duplicating bb %i: both successors are in loop.\n", loop->num); - return false; + return ch_impossible; } /* If this is not the original loop header, we want it to have just @@ -211,7 +228,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, fprintf (dump_file, " Not duplicating bb %i: it has mutiple predecestors.\n", header->index); - return false; + return ch_impossible; } gcond *last = safe_dyn_cast (*gsi_last_bb (header)); @@ -221,7 +238,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, fprintf (dump_file, " Not duplicating bb %i: it does not end by conditional.\n", header->index); - return false; + return ch_impossible; } path_range_query *query = NULL; @@ -233,6 +250,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, query, psi.phi ()); gimple_set_uid (psi.phi (), static_p ? 2 : 0); } + bool code_size_cost = false; /* Count number of instructions and punt on calls. Populate stmts INV flag to later apply heuristics to the @@ -268,7 +286,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, header->index); if (query) delete query; - return false; + return ch_impossible; } /* Classify the stmt is invariant in the loop. */ @@ -343,6 +361,8 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, int insns = estimate_num_insns (last, &eni_size_weights); *limit -= insns; + if (insns) + code_size_cost = true; if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " Acconting stmt as %i insns\n", insns); @@ -354,7 +374,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, header->index); if (query) delete query; - return false; + return ch_impossible; } } @@ -403,7 +423,7 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " Conditional combines static and invariant op.\n"); - return true; + return ch_win; } edge static_exit = static_loop_exit (loop, header, *ranger, query); @@ -435,11 +455,16 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, invariant_exits->add (e); } } - return true; + return ch_win_invariant_exit; } + /* If the static exit fully optimize out, it is win to "duplicate" + it. + + TODO: Even if duplication costs some size we may opt to do so in case + exit probability is significant enough (do partial peeling). */ if (static_exit) - return true; + return code_size_cost ? ch_possible_zero_cost : ch_win; /* We was not able to prove that conditional will be eliminated. */ int insns = estimate_num_insns (last, &eni_size_weights); @@ -453,19 +478,10 @@ should_duplicate_loop_header_p (basic_block header, class loop *loop, fprintf (dump_file, " Not duplicating bb %i contains too many insns.\n", header->index); - return false; + return ch_impossible; } - if (header != loop->header) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, - " Not duplicating bb %i: condition based on non-IV loop" - " variant.\n", header->index); - return false; - } - - return true; + return ch_possible; } /* Checks whether LOOP is a do-while style loop. */ @@ -496,10 +512,11 @@ do_while_loop_p (class loop *loop) "predecessors.\n", loop->num); return false; } + basic_block pred = single_pred (loop->latch); /* If the latch predecessor doesn't exit the loop, it is not a do-while loop. */ - if (!loop_exits_from_bb_p (loop, single_pred (loop->latch))) + if (!loop_exits_from_bb_p (loop, pred)) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, @@ -507,6 +524,18 @@ do_while_loop_p (class loop *loop) "does not exit loop.\n", loop->num); return false; } + gcond *last = safe_dyn_cast (*gsi_last_bb (pred)); + if (last + && (gimple_cond_lhs (last) == boolean_false_node + || gimple_cond_lhs (last) == boolean_true_node) + && gimple_cond_rhs (last) == boolean_false_node) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Loop %i is not do-while loop: latch predecessor " + "contains exit we optimized out.\n", loop->num); + return false; + } if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Loop %i is do-while loop\n", loop->num); @@ -634,9 +663,9 @@ update_profile_after_ch (class loop *loop, } entry_count = e_copy->count (); } - /* Be sure that we seen all edges we are supposed to update. */ - gcc_checking_assert (static_exits->is_empty () - && invariant_exits->is_empty ()); + /* Be sure that we seen all invariant exit edges we are supposed to update. + We may have recorded some static exists we decided to not duplicate. */ + gcc_checking_assert (invariant_exits->is_empty ()); } namespace { @@ -792,16 +821,43 @@ ch_base::copy_headers (function *fun) the header to have just a single successor and copying up to postdominator. */ int nheaders = 0; + int last_win_nheaders = 0; + bool last_win_invariant_exit = false; + ch_decision ret; hash_set *invariant_exits = new hash_set ; hash_set *static_exits = new hash_set ; - while (should_duplicate_loop_header_p (header, loop, ranger, - &remaining_limit, - invariant_exits, - static_exits)) + while ((ret = should_duplicate_loop_header_p (header, loop, ranger, + &remaining_limit, + invariant_exits, + static_exits)) + != ch_impossible) { nheaders++; - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, " Will duplicate bb %i\n", header->index); + if (ret >= ch_win) + { + last_win_nheaders = nheaders; + last_win_invariant_exit = (ret == ch_win_invariant_exit); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " Duplicating bb %i is a win\n", + header->index); + } + /* Duplicate BB if has zero cost but be sure it will not + imply duplication of other BBs. */ + else if (ret == ch_possible_zero_cost + && (last_win_nheaders == nheaders - 1 + || (last_win_nheaders == nheaders - 2 + && last_win_invariant_exit))) + { + last_win_nheaders = nheaders; + last_win_invariant_exit = false; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + " Duplicating bb %i is a win; it has zero cost\n", + header->index); + } + else + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " May duplicate bb %i\n", header->index); /* Find a successor of header that is inside a loop; i.e. the new header after the condition is copied. */ @@ -811,8 +867,27 @@ ch_base::copy_headers (function *fun) header = EDGE_SUCC (header, 1)->dest; } - if (nheaders) - candidates.safe_push ({loop, nheaders, invariant_exits, static_exits}); + /* Try to turn loop into do-while. This means ensuring that + last duplicated header will not have loop invariant exit. */ + if (last_win_nheaders && last_win_invariant_exit + && nheaders > last_win_nheaders) + { + last_win_nheaders++; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + " Duplicating additional BB to obtain do-while loop\n"); + } + else if (!last_win_nheaders && nheaders && !do_while_loop_p (loop)) + { + last_win_nheaders = 1; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + " Duplicating header BB to obtain do-while loop\n"); + } + + if (last_win_nheaders) + candidates.safe_push ({loop, last_win_nheaders, + invariant_exits, static_exits}); else { delete invariant_exits; @@ -844,6 +919,8 @@ ch_base::copy_headers (function *fun) entry_count += e->count (); while (n_bbs < candidate.nheaders) { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " Will duplicate bb %i\n", header->index); /* Find a successor of header that is inside a loop; i.e. the new header after the condition is copied. */ if (flow_bb_inside_loop_p (loop, EDGE_SUCC (header, 0)->dest)) @@ -1111,9 +1188,9 @@ pass_ch_vect::execute (function *fun) /* Apply header copying according to a very simple test of do-while shape. */ bool -pass_ch::process_loop_p (class loop *loop) +pass_ch::process_loop_p (class loop *) { - return !do_while_loop_p (loop); + return true; } /* Apply header-copying to loops where we might enable vectorization. */