aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-cfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-cfg.c')
-rw-r--r--gcc/tree-cfg.c137
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. */