diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-18.c b/gcc/testsuite/gcc.dg/loop-unswitch-18.c new file mode 100644 index 0000000000000000000000000000000000000000..91dc2014922c65640c4ed0825890a2afe6367249 --- /dev/null +++ b/gcc/testsuite/gcc.dg/loop-unswitch-18.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-tree-unswitch-optimized" } */ + +void bar(); +void foo (int x, int n, int m) +{ + for (int i = 0; i < n; ++i) + for (int j = 0; j < m; ++j) + if (x) + bar (); +} + +/* { dg-final { scan-tree-dump "unswitching outer loop" "unswitch" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/pr71462.c b/gcc/testsuite/gcc.dg/torture/pr71462.c index 390b88673e39b592822e8648c7694a125e9483fb..996596321ca63f794916c514fd8e94163d6ce9af 100644 --- a/gcc/testsuite/gcc.dg/torture/pr71462.c +++ b/gcc/testsuite/gcc.dg/torture/pr71462.c @@ -1,4 +1,5 @@ /* { dg-do compile } */ +/* { dg-additional-options "-w" } */ short a; long b; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/loopclosedphi.c b/gcc/testsuite/gcc.dg/tree-ssa/loopclosedphi.c index d71b757fbca59b478d1577da241077deadfc2c16..0a8a4174f5fe0e5f5075f3cd78cf5e85e1105e6a 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/loopclosedphi.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/loopclosedphi.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O3 -fno-tree-ch -w -fdump-tree-loopdone-details" } */ +/* { dg-options "-O3 -fno-tree-ch -fno-unswitch-loops -w -fdump-tree-loopdone-details" } */ void t6 (int qz, int wh) @@ -18,4 +18,4 @@ t6 (int qz, int wh) qz = jl * wh; } -/* { dg-final { scan-tree-dump-times "Replacing" 2 "loopdone"} } */ +/* { dg-final { scan-tree-dump-times "Replacing" 3 "loopdone"} } */ diff --git a/gcc/tree-ssa-loop-unswitch.cc b/gcc/tree-ssa-loop-unswitch.cc index dfe75f52f12a6c006affcf0a58c98b997edc5a7d..186ae953f04ec69a5b2e36c6e6057c75def7153b 100644 --- a/gcc/tree-ssa-loop-unswitch.cc +++ b/gcc/tree-ssa-loop-unswitch.cc @@ -217,6 +217,7 @@ static bool tree_unswitch_single_loop (class loop *, dump_user_location_t, basic_block = NULL); static void find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, + class loop *&outer_loop, vec<unswitch_predicate *> &candidates, unswitch_predicate *&hottest, basic_block &hottest_bb); @@ -249,36 +250,31 @@ set_predicates_for_bb (basic_block bb, vec<unswitch_predicate *> predicates) } /* Initialize LOOP information reused during the unswitching pass. - Return total number of instructions in the loop. */ + Return total number of instructions in the loop. Adjusts LOOP to + the outermost loop all candidates are invariant in. */ static unsigned -init_loop_unswitch_info (class loop *loop, unswitch_predicate *&hottest, +init_loop_unswitch_info (class loop *&loop, unswitch_predicate *&hottest, basic_block &hottest_bb) { unsigned total_insns = 0; - /* Calculate instruction count. */ basic_block *bbs = get_loop_body (loop); - for (unsigned i = 0; i < loop->num_nodes; i++) - { - unsigned insns = 0; - for (gimple_stmt_iterator gsi = gsi_start_bb (bbs[i]); !gsi_end_p (gsi); - gsi_next (&gsi)) - insns += estimate_num_insns (gsi_stmt (gsi), &eni_size_weights); - - bbs[i]->aux = (void *)(uintptr_t)insns; - total_insns += insns; - } + /* Unswitch only nests with no sibling loops. */ + class loop *outer_loop = loop; + while (loop_outer (outer_loop)->num != 0 + && !loop_outer (outer_loop)->inner->next) + outer_loop = loop_outer (outer_loop); hottest = NULL; hottest_bb = NULL; - /* Find all unswitching candidates. */ + /* Find all unswitching candidates in the innermost loop. */ for (unsigned i = 0; i != loop->num_nodes; i++) { /* Find a bb to unswitch on. */ vec<unswitch_predicate *> candidates; candidates.create (1); - find_unswitching_predicates_for_bb (bbs[i], loop, candidates, + find_unswitching_predicates_for_bb (bbs[i], loop, outer_loop, candidates, hottest, hottest_bb); if (!candidates.is_empty ()) set_predicates_for_bb (bbs[i], candidates); @@ -291,8 +287,34 @@ init_loop_unswitch_info (class loop *loop, unswitch_predicate *&hottest, } } + if (outer_loop != loop) + { + free (bbs); + bbs = get_loop_body (outer_loop); + } + + /* Calculate instruction count. */ + for (unsigned i = 0; i < outer_loop->num_nodes; i++) + { + unsigned insns = 0; + for (gimple_stmt_iterator gsi = gsi_start_bb (bbs[i]); !gsi_end_p (gsi); + gsi_next (&gsi)) + insns += estimate_num_insns (gsi_stmt (gsi), &eni_size_weights); + /* No predicates to unswitch on in the outer loops. */ + if (!flow_bb_inside_loop_p (loop, bbs[i])) + { + gimple *last = last_stmt (bbs[i]); + if (last != NULL) + gimple_set_uid (last, 0); + } + + bbs[i]->aux = (void *)(uintptr_t)insns; + total_insns += insns; + } + free (bbs); + loop = outer_loop; return total_insns; } @@ -307,72 +329,74 @@ tree_ssa_unswitch_loops (function *fun) ranger = enable_ranger (fun); - /* Go through all loops starting from innermost. */ + /* Go through all loops starting from innermost, hoisting guards. */ for (auto loop : loops_list (fun, LI_FROM_INNERMOST)) { - if (!loop->inner) - { - /* Perform initial tests if unswitch is eligible. */ - dump_user_location_t loc = find_loop_location (loop); + if (loop->inner) + changed_hoist |= tree_unswitch_outer_loop (loop); + } - /* Do not unswitch in cold regions. */ - if (optimize_loop_for_size_p (loop)) - { - if (dump_enabled_p ()) - dump_printf_loc (MSG_NOTE, loc, - "Not unswitching cold loops\n"); - continue; - } + /* Go through innermost loops, unswitching on invariant predicates + within those. */ + for (auto loop : loops_list (fun, LI_ONLY_INNERMOST)) + { + /* Perform initial tests if unswitch is eligible. */ + dump_user_location_t loc = find_loop_location (loop); - /* If the loop is not expected to iterate, there is no need - for unswitching. */ - HOST_WIDE_INT iterations = estimated_loop_iterations_int (loop); - if (iterations < 0) - iterations = likely_max_loop_iterations_int (loop); - if (iterations >= 0 && iterations <= 1) - { - if (dump_enabled_p ()) - dump_printf_loc (MSG_NOTE, loc, - "Not unswitching, loop is not expected" - " to iterate\n"); - continue; - } + /* Do not unswitch in cold regions. */ + if (optimize_loop_for_size_p (loop)) + { + if (dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, loc, + "Not unswitching cold loops\n"); + continue; + } - bb_predicates = new vec<vec<unswitch_predicate *>> (); - bb_predicates->safe_push (vec<unswitch_predicate *> ()); - unswitch_predicate::predicates = new vec<unswitch_predicate *> (); - - /* Unswitch innermost loop. */ - unswitch_predicate *hottest; - basic_block hottest_bb; - unsigned int loop_size = init_loop_unswitch_info (loop, hottest, - hottest_bb); - unsigned int budget = loop_size + param_max_unswitch_insns; - - predicate_vector predicate_path; - predicate_path.create (8); - auto_bitmap handled; - changed_unswitch - |= tree_unswitch_single_loop (loop, loc, predicate_path, - loop_size, budget, - ignored_edge_flag, handled, - hottest, hottest_bb); - predicate_path.release (); - - for (auto predlist : bb_predicates) - predlist.release (); - bb_predicates->release (); - delete bb_predicates; - bb_predicates = NULL; - - for (auto pred : unswitch_predicate::predicates) - delete pred; - unswitch_predicate::predicates->release (); - delete unswitch_predicate::predicates; - unswitch_predicate::predicates = NULL; + /* If the loop is not expected to iterate, there is no need + for unswitching. */ + HOST_WIDE_INT iterations = estimated_loop_iterations_int (loop); + if (iterations < 0) + iterations = likely_max_loop_iterations_int (loop); + if (iterations >= 0 && iterations <= 1) + { + if (dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, loc, + "Not unswitching, loop is not expected" + " to iterate\n"); + continue; } - else - changed_hoist |= tree_unswitch_outer_loop (loop); + + bb_predicates = new vec<vec<unswitch_predicate *>> (); + bb_predicates->safe_push (vec<unswitch_predicate *> ()); + unswitch_predicate::predicates = new vec<unswitch_predicate *> (); + + /* Unswitch loop. */ + unswitch_predicate *hottest; + basic_block hottest_bb; + unsigned int loop_size = init_loop_unswitch_info (loop, hottest, + hottest_bb); + unsigned int budget = loop_size + param_max_unswitch_insns; + + predicate_vector predicate_path; + predicate_path.create (8); + auto_bitmap handled; + changed_unswitch |= tree_unswitch_single_loop (loop, loc, predicate_path, + loop_size, budget, + ignored_edge_flag, handled, + hottest, hottest_bb); + predicate_path.release (); + + for (auto predlist : bb_predicates) + predlist.release (); + bb_predicates->release (); + delete bb_predicates; + bb_predicates = NULL; + + for (auto pred : unswitch_predicate::predicates) + delete pred; + unswitch_predicate::predicates->release (); + delete unswitch_predicate::predicates; + unswitch_predicate::predicates = NULL; } disable_ranger (fun); @@ -463,10 +487,13 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop) /* Checks whether we can unswitch LOOP on condition at end of BB -- one of its basic blocks (for what it means see comments below). - All candidates all filled to the provided vector CANDIDATES. */ + All candidates all filled to the provided vector CANDIDATES. + OUTER_LOOP is updated to the innermost loop all found candidates are + invariant in. */ static void find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, + class loop *&outer_loop, vec<unswitch_predicate *> &candidates, unswitch_predicate *&hottest, basic_block &hottest_bb) @@ -506,6 +533,18 @@ find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, if (is_maybe_undefined (use, stmt, loop)) return; } + /* Narrow OUTER_LOOP. */ + if (outer_loop != loop) + FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) + { + def = SSA_NAME_DEF_STMT (use); + def_bb = gimple_bb (def); + while (outer_loop != loop + && ((def_bb && flow_bb_inside_loop_p (outer_loop, def_bb)) + || is_maybe_undefined (use, stmt, outer_loop))) + outer_loop = superloop_at_depth (loop, + loop_depth (outer_loop) + 1); + } unswitch_predicate *predicate = new unswitch_predicate (stmt); candidates.safe_push (predicate); @@ -535,6 +574,12 @@ find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, behavior that the original program might never exercise. */ if (is_maybe_undefined (idx, stmt, loop)) return; + /* Narrow OUTER_LOOP. */ + while (outer_loop != loop + && ((def_bb && flow_bb_inside_loop_p (outer_loop, def_bb)) + || is_maybe_undefined (idx, stmt, outer_loop))) + outer_loop = superloop_at_depth (loop, + loop_depth (outer_loop) + 1); /* Build compound expression for all outgoing edges of the switch. */ auto_vec<tree, 16> preds; @@ -872,7 +917,7 @@ evaluate_bbs (class loop *loop, predicate_vector *predicate_path, { basic_block dest = e->dest; - if (dest->loop_father == loop + if (flow_bb_inside_loop_p (loop, dest) && !(dest->flags & reachable_flag) && !(e->flags & flags) && !ignored_edges.contains (e)) @@ -992,7 +1037,8 @@ tree_unswitch_single_loop (class loop *loop, dump_user_location_t loc, if (dump_enabled_p ()) { dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, - "unswitching loop %d on %qs with condition: %T\n", + "unswitching %sloop %d on %qs with condition: %T\n", + loop->inner ? "outer " : "", loop->num, predicate->switch_p ? "switch" : "if", predicate->condition); dump_printf_loc (MSG_NOTE, loc, @@ -1064,7 +1110,6 @@ tree_unswitch_loop (class loop *loop, edge edge_true, tree cond) /* Some sanity checking. */ gcc_assert (flow_bb_inside_loop_p (loop, edge_true->src)); gcc_assert (EDGE_COUNT (edge_true->src->succs) >= 2); - gcc_assert (loop->inner == NULL); profile_probability prob_true = edge_true->probability; return loop_version (loop, unshare_expr (cond),