diff options
Diffstat (limited to 'gcc/tree-cfg.c')
-rw-r--r-- | gcc/tree-cfg.c | 137 |
1 files changed, 126 insertions, 11 deletions
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 1c820a3e3c5..4a34cefd2a4 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -4850,6 +4850,31 @@ gimple_duplicate_bb (basic_block bb) return new_bb; } +/* Add phi arguments to the phi nodes in E_COPY->dest according to + the phi arguments coming from the equivalent edge at + the phi nodes of DEST. */ + +static void +add_phi_args_after_redirect (edge e_copy, edge orig_e) +{ + gimple_stmt_iterator psi, psi_copy; + gimple phi, phi_copy; + tree def; + + for (psi = gsi_start_phis (orig_e->dest), + psi_copy = gsi_start_phis (e_copy->dest); + !gsi_end_p (psi); + gsi_next (&psi), gsi_next (&psi_copy)) + { + + phi = gsi_stmt (psi); + phi_copy = gsi_stmt (psi_copy); + def = PHI_ARG_DEF_FROM_EDGE (phi, orig_e); + add_phi_arg (phi_copy, def, e_copy, + gimple_phi_arg_location_from_edge (phi, orig_e)); + } +} + /* Adds phi node arguments for edge E_COPY after basic block duplication. */ static void @@ -5131,9 +5156,14 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU int total_freq = 0, exit_freq = 0; gcov_type total_count = 0, exit_count = 0; edge exits[2], nexits[2], e; - gimple_stmt_iterator gsi; + gimple_stmt_iterator gsi,gsi1; gimple cond_stmt; - edge sorig, snew; + edge sorig, snew, orig_e; + basic_block exit_bb; + edge_iterator ei; + VEC (edge, heap) *redirect_edges; + basic_block iters_bb, orig_src; + tree new_rhs; gcc_assert (EDGE_COUNT (exit->src->succs) == 2); exits[0] = exit; @@ -5149,17 +5179,13 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU it will work, but the resulting code will not be correct. */ for (i = 0; i < n_region; i++) { - /* We do not handle subloops, i.e. all the blocks must belong to the - same loop. */ - if (region[i]->loop_father != orig_loop) - return false; - if (region[i] == orig_loop->latch) return false; } initialize_original_copy_tables (); set_loop_copy (orig_loop, loop); + duplicate_subloops (orig_loop, loop); if (!region_copy) { @@ -5225,8 +5251,36 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU cond_stmt = last_stmt (exit->src); gcc_assert (gimple_code (cond_stmt) == GIMPLE_COND); cond_stmt = gimple_copy (cond_stmt); + + /* If the block consisting of the exit condition has the latch as + successor, then the body of the loop is executed before + the exit condition is tested. In such case, moving the + condition to the entry, causes that the loop will iterate + one less iteration (which is the wanted outcome, since we + peel out the last iteration). If the body is executed after + the condition, moving the condition to the entry requires + decrementing one iteration. */ + if (exits[1]->dest == orig_loop->latch) + new_rhs = gimple_cond_rhs (cond_stmt); + else + { + new_rhs = fold_build2 (MINUS_EXPR, TREE_TYPE (gimple_cond_rhs (cond_stmt)), + gimple_cond_rhs (cond_stmt), + build_int_cst (TREE_TYPE (gimple_cond_rhs (cond_stmt)), 1)); + + if (TREE_CODE (gimple_cond_rhs (cond_stmt)) == SSA_NAME) + { + iters_bb = gimple_bb (SSA_NAME_DEF_STMT (gimple_cond_rhs (cond_stmt))); + for (gsi1 = gsi_start_bb (iters_bb); !gsi_end_p (gsi1); gsi_next (&gsi1)) + if (gsi_stmt (gsi1) == SSA_NAME_DEF_STMT (gimple_cond_rhs (cond_stmt))) + break; + + new_rhs = force_gimple_operand_gsi (&gsi1, new_rhs, true, + NULL_TREE,false,GSI_CONTINUE_LINKING); + } + } + gimple_cond_set_rhs (cond_stmt, unshare_expr (new_rhs)); gimple_cond_set_lhs (cond_stmt, unshare_expr (gimple_cond_lhs (cond_stmt))); - gimple_cond_set_rhs (cond_stmt, unshare_expr (gimple_cond_rhs (cond_stmt))); gsi_insert_after (&gsi, cond_stmt, GSI_NEW_STMT); sorig = single_succ_edge (switch_bb); @@ -5238,13 +5292,74 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU /* Add the PHI node arguments. */ add_phi_args_after_copy (region_copy, n_region, snew); - + /* Get rid of now superfluous conditions and associated edges (and phi node arguments). */ + exit_bb = exit->dest; + e = redirect_edge_and_branch (exits[0], exits[1]->dest); PENDING_STMT (e) = NULL; - e = redirect_edge_and_branch (nexits[1], nexits[0]->dest); - PENDING_STMT (e) = NULL; + + /* If the block consisting of the exit condition has the latch as + successor, then the body of the loop is executed before + the exit condition is tested. + + { body } + { cond } (exit[0]) -> { latch } + | + V (exit[1]) + + { exit_bb } + + + In such case, the equivalent copied edge nexits[1] + (for the peeled iteration) needs to be redirected to exit_bb. + + Otherwise, + + { cond } (exit[0]) -> { body } + | + V (exit[1]) + + { exit_bb } + + + exit[0] is pointing to the body of the loop, + and the equivalent nexits[0] needs to be redirected to + the copied body (of the peeled iteration). */ + + if (exits[1]->dest == orig_loop->latch) + e = redirect_edge_and_branch (nexits[1], nexits[0]->dest); + else + e = redirect_edge_and_branch (nexits[0], nexits[1]->dest); + PENDING_STMT (e) = NULL; + + redirect_edges = VEC_alloc (edge, heap, 10); + + for (i = 0; i < n_region; i++) + region_copy[i]->flags |= BB_DUPLICATED; + + /* Iterate all incoming edges to latch. All those coming from + copied bbs will be redirected to exit_bb. */ + FOR_EACH_EDGE (e, ei, orig_loop->latch->preds) + { + if (e->src->flags & BB_DUPLICATED) + VEC_safe_push (edge, heap, redirect_edges, e); + } + + for (i = 0; i < n_region; i++) + region_copy[i]->flags &= ~BB_DUPLICATED; + + for (i = 0; VEC_iterate (edge, redirect_edges, i, e); ++i) + { + e = redirect_edge_and_branch (e, exit_bb); + PENDING_STMT (e) = NULL; + orig_src = get_bb_original (e->src); + orig_e = find_edge (orig_src, orig_loop->latch); + add_phi_args_after_redirect (e, orig_e); + } + + VEC_free (edge, heap, redirect_edges); /* Anything that is outside of the region, but was dominated by something inside needs to update dominance info. */ |