diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1d672944981732fce743cc9b1c3485b26dab41cf..793c4933f79fb0f02472b05e908a5cecf45409e6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2012-11-02 Jan Hubicka <jh@suse.cz> + + PR middle-end/55079 + * tree-ssa-loop-niter.c (number_of_iterations_exit): Update + MAX field if NITER was folded to contant. + (record_estimate): Sanity check. + * tree-ssa-loop-ivcanon.c (remove_exits_and_undefined_stmts): New + function. + (remove_redundant_iv_test): New function. + (loops_to_unloop, loops_to_unloop_nunroll): New static vars. + (unloop_loops): Break out from ... + (try_unroll_loop_completely): ... here; Pass in MAXITER; use + remove_exits_and_undefined_stmts; do not unloop. + (canonicalize_loop_induction_variables): Compute MAXITER; + use remove_redundant_iv_test; remove loop_close_ssa_invalidated + and irred_invalidated arguments. + (canonicalize_induction_variables): Compute fresh bound estimates; + unloop; walk from innermost. + (tree_unroll_loops_completely): Likewise. + 2012-11-02 Vladimir Makarov <vmakarov@redhat.com> PR middle-end/55130 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 26fec16950ef32fca2444bd59bfa6ad60642faa5..cc75c0b64756f318cff1a60dab51d97e0f743589 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2012-11-02 Jan Hubicka <jh@suse.cz> + + * gcc.dg/tree-ssa/cunroll-10.c: New testcase. + * gcc.dg/tree-ssa/cunroll-9.c: New testcase. + 2012-11-02 Vladimir Makarov <vmakarov@redhat.com> PR middle-end/55130 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c new file mode 100644 index 0000000000000000000000000000000000000000..7893565918a68387c462d265072a589787b45a48 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -Warray-bounds -fdump-tree-cunroll-details" } */ +int a[3]; +int b[4]; +main() +{ + int i; + for (i=0;i<4;i++) + if (b[i]==2) + a[i]++; +} +/* { dg-final { scan-tree-dump-times "Forced statement unreachable" 2 "cunroll" } } */ +/* { dg-final { cleanup-tree-dump "cunroll" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c new file mode 100644 index 0000000000000000000000000000000000000000..3205b8d396515d1f0e4b76547b790cd2b87974e7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-cunrolli" } */ +void abort (void); +int a[10]; +int b[11]; +t (int n) +{ + int i; + int sum = 0; + for (i = 0; i < n; i++) + { + if (i > 1000) + abort (); + if (q ()) + sum += a[i]; + else + sum += b[i]; + } + return sum; +} +/* { dg-final { scan-tree-dump-times 1 "Removed pointless exit:" "cunroli" } } */ +/* { dg-final { cleanup-tree-dump "cunroli" } } */ diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c index 6f37f37cbaaf52ac5c45e8ccb61d3d9b8ab23387..433bb7783cf3320e54af5ca18b2f1ebd2eec49c4 100644 --- a/gcc/tree-ssa-loop-ivcanon.c +++ b/gcc/tree-ssa-loop-ivcanon.c @@ -389,33 +389,192 @@ loop_edge_to_cancel (struct loop *loop) return NULL; } -/* Tries to unroll LOOP completely, i.e. NITER times. - UL determines which loops we are allowed to unroll. - EXIT is the exit of the loop that should be eliminated. +/* Remove all tests for exits that are known to be taken after LOOP was + peeled NPEELED times. Put gcc_unreachable before every statement + known to not be executed. */ + +static bool +remove_exits_and_undefined_stmts (struct loop *loop, unsigned int npeeled) +{ + struct nb_iter_bound *elt; + bool changed = false; + + for (elt = loop->bounds; elt; elt = elt->next) + { + /* If statement is known to be undefined after peeling, turn it + into unreachable (or trap when debugging experience is supposed + to be good). */ + if (!elt->is_exit + && elt->bound.ult (double_int::from_uhwi (npeeled))) + { + gimple_stmt_iterator gsi = gsi_for_stmt (elt->stmt); + gimple stmt = gimple_build_call + (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); + + gimple_set_location (stmt, gimple_location (elt->stmt)); + gsi_insert_before (&gsi, stmt, GSI_NEW_STMT); + changed = true; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Forced statement unreachable: "); + print_gimple_stmt (dump_file, elt->stmt, 0, 0); + } + } + /* If we know the exit will be taken after peeling, update. */ + else if (elt->is_exit + && elt->bound.ule (double_int::from_uhwi (npeeled))) + { + basic_block bb = gimple_bb (elt->stmt); + edge exit_edge = EDGE_SUCC (bb, 0); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Forced exit to be taken: "); + print_gimple_stmt (dump_file, elt->stmt, 0, 0); + } + if (!loop_exit_edge_p (loop, exit_edge)) + exit_edge = EDGE_SUCC (bb, 1); + gcc_checking_assert (loop_exit_edge_p (loop, exit_edge)); + if (exit_edge->flags & EDGE_TRUE_VALUE) + gimple_cond_make_true (elt->stmt); + else + gimple_cond_make_false (elt->stmt); + update_stmt (elt->stmt); + changed = true; + } + } + return changed; +} + +/* Remove all exits that are known to be never taken because of the loop bound + discovered. */ + +static bool +remove_redundant_iv_tests (struct loop *loop) +{ + struct nb_iter_bound *elt; + bool changed = false; + + if (!loop->any_upper_bound) + return false; + for (elt = loop->bounds; elt; elt = elt->next) + { + /* Exit is pointless if it won't be taken before loop reaches + upper bound. */ + if (elt->is_exit && loop->any_upper_bound + && loop->nb_iterations_upper_bound.ult (elt->bound)) + { + basic_block bb = gimple_bb (elt->stmt); + edge exit_edge = EDGE_SUCC (bb, 0); + struct tree_niter_desc niter; + + if (!loop_exit_edge_p (loop, exit_edge)) + exit_edge = EDGE_SUCC (bb, 1); + + /* Only when we know the actual number of iterations, not + just a bound, we can remove the exit. */ + if (!number_of_iterations_exit (loop, exit_edge, + &niter, false, false)) + gcc_unreachable (); + if (!integer_onep (niter.assumptions) + || !integer_zerop (niter.may_be_zero) + || !niter.niter + || TREE_CODE (niter.niter) != INTEGER_CST + || !loop->nb_iterations_upper_bound.ult + (tree_to_double_int (niter.niter))) + continue; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Removed pointless exit: "); + print_gimple_stmt (dump_file, elt->stmt, 0, 0); + } + if (exit_edge->flags & EDGE_TRUE_VALUE) + gimple_cond_make_false (elt->stmt); + else + gimple_cond_make_true (elt->stmt); + update_stmt (elt->stmt); + changed = true; + } + } + return changed; +} + +/* Stores loops that will be unlooped after we process whole loop tree. */ +static VEC(loop_p, heap) *loops_to_unloop; +static VEC(int, heap) *loops_to_unloop_nunroll; + +/* Cancel all fully unrolled loops by putting __builtin_unreachable + on the latch edge. + We do it after all unrolling since unlooping moves basic blocks + across loop boundaries trashing loop closed SSA form as well + as SCEV info needed to be intact during unrolling. + IRRED_INVALIDATED is used to bookkeep if information about irreducible regions may become invalid as a result of the transformation. LOOP_CLOSED_SSA_INVALIDATED is used to bookkepp the case when we need to go into loop closed SSA form. */ +void +unloop_loops (bitmap loop_closed_ssa_invalidated, + bool *irred_invalidated) +{ + while (VEC_length (loop_p, loops_to_unloop)) + { + struct loop *loop = VEC_pop (loop_p, loops_to_unloop); + int n_unroll = VEC_pop (int, loops_to_unloop_nunroll); + basic_block latch = loop->latch; + edge latch_edge = loop_latch_edge (loop); + int flags = latch_edge->flags; + location_t locus = latch_edge->goto_locus; + gimple stmt; + gimple_stmt_iterator gsi; + + remove_exits_and_undefined_stmts (loop, n_unroll); + + /* Unloop destroys the latch edge. */ + unloop (loop, irred_invalidated, loop_closed_ssa_invalidated); + + /* Create new basic block for the latch edge destination and wire + it in. */ + stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); + latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags); + latch_edge->probability = 0; + latch_edge->count = 0; + latch_edge->flags |= flags; + latch_edge->goto_locus = locus; + + latch_edge->dest->loop_father = current_loops->tree_root; + latch_edge->dest->count = 0; + latch_edge->dest->frequency = 0; + set_immediate_dominator (CDI_DOMINATORS, latch_edge->dest, latch_edge->src); + + gsi = gsi_start_bb (latch_edge->dest); + gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); + } + VEC_free (loop_p, heap, loops_to_unloop); + loops_to_unloop = NULL; + VEC_free (int, heap, loops_to_unloop_nunroll); + loops_to_unloop_nunroll = NULL; +} + +/* Tries to unroll LOOP completely, i.e. NITER times. + UL determines which loops we are allowed to unroll. + EXIT is the exit of the loop that should be eliminated. + MAXITER specfy bound on number of iterations, -1 if it is + not known or too large for HOST_WIDE_INT. */ + static bool try_unroll_loop_completely (struct loop *loop, edge exit, tree niter, enum unroll_level ul, - bool *irred_invalidated, - bitmap loop_closed_ssa_invalidated) + HOST_WIDE_INT maxiter) { unsigned HOST_WIDE_INT n_unroll, ninsns, max_unroll, unr_insns; gimple cond; struct loop_size size; bool n_unroll_found = false; - HOST_WIDE_INT maxiter; - basic_block latch; - edge latch_edge; - location_t locus; - int flags; - gimple stmt; - gimple_stmt_iterator gsi; edge edge_to_cancel = NULL; int num = loop->num; @@ -445,7 +604,6 @@ try_unroll_loop_completely (struct loop *loop, exit = NULL; /* See if we can improve our estimate by using recorded loop bounds. */ - maxiter = max_loop_iterations_int (loop); if (maxiter >= 0 && (!n_unroll_found || (unsigned HOST_WIDE_INT)maxiter < n_unroll)) { @@ -545,6 +703,7 @@ try_unroll_loop_completely (struct loop *loop, free_original_copy_tables (); } + /* Remove the conditional from the last copy of the loop. */ if (edge_to_cancel) { @@ -557,37 +716,10 @@ try_unroll_loop_completely (struct loop *loop, /* Do not remove the path. Doing so may remove outer loop and confuse bookkeeping code in tree_unroll_loops_completelly. */ } - /* We did not manage to cancel the loop. - The loop latch remains reachable even if it will never be reached - at runtime. We must redirect it to somewhere, so create basic - block containg __builtin_unreachable call for this reason. */ - else - { - latch = loop->latch; - latch_edge = loop_latch_edge (loop); - flags = latch_edge->flags; - locus = latch_edge->goto_locus; - /* Unloop destroys the latch edge. */ - unloop (loop, irred_invalidated, loop_closed_ssa_invalidated); - - /* Create new basic block for the latch edge destination and wire - it in. */ - stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); - latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags); - latch_edge->probability = 0; - latch_edge->count = 0; - latch_edge->flags |= flags; - latch_edge->goto_locus = locus; - - latch_edge->dest->loop_father = current_loops->tree_root; - latch_edge->dest->count = 0; - latch_edge->dest->frequency = 0; - set_immediate_dominator (CDI_DOMINATORS, latch_edge->dest, latch_edge->src); - - gsi = gsi_start_bb (latch_edge->dest); - gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); - } + /* Store the loop for later unlooping and exit removal. */ + VEC_safe_push (loop_p, heap, loops_to_unloop, loop); + VEC_safe_push (int, heap, loops_to_unloop_nunroll, n_unroll); if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -614,19 +746,17 @@ try_unroll_loop_completely (struct loop *loop, CREATE_IV is true if we may create a new iv. UL determines which loops we are allowed to completely unroll. If TRY_EVAL is true, we try to determine the number of iterations of a loop by direct evaluation. - Returns true if cfg is changed. - - IRRED_INVALIDATED is used to keep if irreducible reginos needs to be recomputed. */ + Returns true if cfg is changed. */ static bool canonicalize_loop_induction_variables (struct loop *loop, bool create_iv, enum unroll_level ul, - bool try_eval, - bool *irred_invalidated, - bitmap loop_closed_ssa_invalidated) + bool try_eval) { edge exit = NULL; tree niter; + HOST_WIDE_INT maxiter; + bool modified = false; niter = number_of_latch_executions (loop); if (TREE_CODE (niter) == INTEGER_CST) @@ -657,6 +787,9 @@ canonicalize_loop_induction_variables (struct loop *loop, if (niter && TREE_CODE (niter) == INTEGER_CST) record_niter_bound (loop, tree_to_double_int (niter), false, true); + /* Force re-computation of loop bounds so we can remove redundant exits. */ + maxiter = max_loop_iterations_int (loop); + if (dump_file && (dump_flags & TDF_DETAILS) && TREE_CODE (niter) == INTEGER_CST) { @@ -665,21 +798,25 @@ canonicalize_loop_induction_variables (struct loop *loop, fprintf (dump_file, " times.\n"); } if (dump_file && (dump_flags & TDF_DETAILS) - && max_loop_iterations_int (loop) >= 0) + && maxiter >= 0) { fprintf (dump_file, "Loop %d iterates at most %i times.\n", loop->num, - (int)max_loop_iterations_int (loop)); + (int)maxiter); } - if (try_unroll_loop_completely (loop, exit, niter, ul, irred_invalidated, - loop_closed_ssa_invalidated)) + /* Remove exits that are known to be never taken based on loop bound. + Needs to be called after compilation of max_loop_iterations_int that + populates the loop bounds. */ + modified |= remove_redundant_iv_tests (loop); + + if (try_unroll_loop_completely (loop, exit, niter, ul, maxiter)) return true; if (create_iv && niter && !chrec_contains_undetermined (niter)) create_canonical_iv (loop, exit, niter); - return false; + return modified; } /* The main entry point of the pass. Adds canonical induction variables @@ -694,16 +831,18 @@ canonicalize_induction_variables (void) bool irred_invalidated = false; bitmap loop_closed_ssa_invalidated = BITMAP_ALLOC (NULL); - FOR_EACH_LOOP (li, loop, 0) + free_numbers_of_iterations_estimates (); + estimate_numbers_of_iterations (); + + FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST) { changed |= canonicalize_loop_induction_variables (loop, true, UL_SINGLE_ITER, - true, - &irred_invalidated, - loop_closed_ssa_invalidated); + true); } gcc_assert (!need_ssa_update_p (cfun)); + unloop_loops (loop_closed_ssa_invalidated, &irred_invalidated); if (irred_invalidated && loops_state_satisfies_p (LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS)) mark_irreducible_loops (); @@ -822,7 +961,10 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer) if (loops_state_satisfies_p (LOOP_CLOSED_SSA)) loop_closed_ssa_invalidated = BITMAP_ALLOC (NULL); - FOR_EACH_LOOP (li, loop, 0) + free_numbers_of_iterations_estimates (); + estimate_numbers_of_iterations (); + + FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST) { struct loop *loop_father = loop_outer (loop); @@ -835,8 +977,7 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer) ul = UL_NO_GROWTH; if (canonicalize_loop_induction_variables - (loop, false, ul, !flag_tree_loop_ivcanon, - &irred_invalidated, loop_closed_ssa_invalidated)) + (loop, false, ul, !flag_tree_loop_ivcanon)) { changed = true; /* If we'll continue unrolling, we need to propagate constants @@ -856,8 +997,16 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer) struct loop **iter; unsigned i; - /* We can not use TODO_update_ssa_no_phi because VOPS gets confused. */ + /* Be sure to skip unlooped loops while procesing father_stack + array. */ + FOR_EACH_VEC_ELT (loop_p, loops_to_unloop, i, iter) + (*iter)->aux = NULL; + FOR_EACH_VEC_ELT (loop_p, father_stack, i, iter) + if (!(*iter)->aux) + *iter = NULL; + unloop_loops (loop_closed_ssa_invalidated, &irred_invalidated); + /* We can not use TODO_update_ssa_no_phi because VOPS gets confused. */ if (loop_closed_ssa_invalidated && !bitmap_empty_p (loop_closed_ssa_invalidated)) rewrite_into_loop_closed_ssa (loop_closed_ssa_invalidated, @@ -867,14 +1016,15 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer) /* Propagate the constants within the new basic blocks. */ FOR_EACH_VEC_ELT (loop_p, father_stack, i, iter) - { - unsigned j; - basic_block *body = get_loop_body_in_dom_order (*iter); - for (j = 0; j < (*iter)->num_nodes; j++) - propagate_constants_for_unrolling (body[j]); - free (body); - (*iter)->aux = NULL; - } + if (*iter) + { + unsigned j; + basic_block *body = get_loop_body_in_dom_order (*iter); + for (j = 0; j < (*iter)->num_nodes; j++) + propagate_constants_for_unrolling (body[j]); + free (body); + (*iter)->aux = NULL; + } VEC_truncate (loop_p, father_stack, 0); /* This will take care of removing completely unrolled loops diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c index 20681a9140a33b4837a2ed5b507d2d13fce84b9c..b77bcbbe5bade64ea315c2fa20ad3008689829cd 100644 --- a/gcc/tree-ssa-loop-niter.c +++ b/gcc/tree-ssa-loop-niter.c @@ -1880,6 +1880,10 @@ number_of_iterations_exit (struct loop *loop, edge exit, fold_undefer_and_ignore_overflow_warnings (); + /* If NITER has simplified into a constant, update MAX. */ + if (TREE_CODE (niter->niter) == INTEGER_CST) + niter->max = tree_to_double_int (niter->niter); + if (integer_onep (niter->assumptions)) return true; @@ -2556,6 +2560,8 @@ record_estimate (struct loop *loop, tree bound, double_int i_bound, real number of iterations. */ if (TREE_CODE (bound) != INTEGER_CST) realistic = false; + else + gcc_checking_assert (i_bound == tree_to_double_int (bound)); if (!upper && !realistic) return;