summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-loop-unswitch.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-loop-unswitch.c')
-rw-r--r--gcc/tree-ssa-loop-unswitch.c202
1 files changed, 180 insertions, 22 deletions
diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index ba1f13c320..1845148666 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -1,5 +1,5 @@
/* Loop unswitching.
- Copyright (C) 2004-2016 Free Software Foundation, Inc.
+ Copyright (C) 2004-2017 Free Software Foundation, Inc.
This file is part of GCC.
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-inline.h"
#include "gimple-iterator.h"
#include "cfghooks.h"
+#include "tree-ssa-loop-manip.h"
/* This file implements the loop unswitching, i.e. transformation of loops like
@@ -108,6 +109,80 @@ tree_ssa_unswitch_loops (void)
return 0;
}
+/* Return TRUE if an SSA_NAME maybe undefined and is therefore
+ unsuitable for unswitching. STMT is the statement we are
+ considering for unswitching and LOOP is the loop it appears in. */
+
+static bool
+is_maybe_undefined (const tree name, gimple *stmt, struct loop *loop)
+{
+ /* The loop header is the only block we can trivially determine that
+ will always be executed. If the comparison is in the loop
+ header, we know it's OK to unswitch on it. */
+ if (gimple_bb (stmt) == loop->header)
+ return false;
+
+ auto_bitmap visited_ssa;
+ auto_vec<tree> worklist;
+ worklist.safe_push (name);
+ bitmap_set_bit (visited_ssa, SSA_NAME_VERSION (name));
+ while (!worklist.is_empty ())
+ {
+ tree t = worklist.pop ();
+
+ /* If it's obviously undefined, avoid further computations. */
+ if (ssa_undefined_value_p (t, true))
+ return true;
+
+ if (ssa_defined_default_def_p (t))
+ continue;
+
+ gimple *def = SSA_NAME_DEF_STMT (t);
+
+ /* Check that all the PHI args are fully defined. */
+ if (gphi *phi = dyn_cast <gphi *> (def))
+ {
+ for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+ {
+ tree t = gimple_phi_arg_def (phi, i);
+ /* If an SSA has already been seen, it may be a loop,
+ but we can continue and ignore this use. Otherwise,
+ add the SSA_NAME to the queue and visit it later. */
+ if (TREE_CODE (t) == SSA_NAME
+ && bitmap_set_bit (visited_ssa, SSA_NAME_VERSION (t)))
+ worklist.safe_push (t);
+ }
+ continue;
+ }
+
+ /* Uses in stmts always executed when the region header executes
+ are fine. */
+ if (dominated_by_p (CDI_DOMINATORS, loop->header, gimple_bb (def)))
+ continue;
+
+ /* Handle calls and memory loads conservatively. */
+ if (!is_gimple_assign (def)
+ || (gimple_assign_single_p (def)
+ && gimple_vuse (def)))
+ return true;
+
+ /* Check that any SSA names used to define NAME are also fully
+ defined. */
+ use_operand_p use_p;
+ ssa_op_iter iter;
+ FOR_EACH_SSA_USE_OPERAND (use_p, def, iter, SSA_OP_USE)
+ {
+ tree t = USE_FROM_PTR (use_p);
+ /* If an SSA has already been seen, it may be a loop,
+ but we can continue and ignore this use. Otherwise,
+ add the SSA_NAME to the queue and visit it later. */
+ if (bitmap_set_bit (visited_ssa, SSA_NAME_VERSION (t)))
+ worklist.safe_push (t);
+ }
+ }
+ return false;
+}
+
/* Checks whether we can unswitch LOOP on condition at end of BB -- one of its
basic blocks (for what it means see comments below). */
@@ -135,15 +210,15 @@ tree_may_unswitch_on (basic_block bb, struct loop *loop)
/* Condition must be invariant. */
FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
{
- /* Unswitching on undefined values would introduce undefined
- behavior that the original program might never exercise. */
- if (ssa_undefined_value_p (use, true))
- return NULL_TREE;
def = SSA_NAME_DEF_STMT (use);
def_bb = gimple_bb (def);
if (def_bb
&& flow_bb_inside_loop_p (loop, def_bb))
return NULL_TREE;
+ /* Unswitching on undefined values would introduce undefined
+ behavior that the original program might never exercise. */
+ if (is_maybe_undefined (use, stmt, loop))
+ return NULL_TREE;
}
cond = build2 (gimple_cond_code (stmt), boolean_type_node,
@@ -223,6 +298,8 @@ tree_unswitch_single_loop (struct loop *loop, int num)
/* If the loop is not expected to iterate, there is no need
for unswitching. */
iterations = estimated_loop_iterations_int (loop);
+ if (iterations < 0)
+ iterations = likely_max_loop_iterations_int (loop);
if (iterations >= 0 && iterations <= 1)
{
if (dump_file && (dump_flags & TDF_DETAILS))
@@ -414,7 +491,7 @@ tree_unswitch_loop (struct loop *loop,
extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);
prob_true = edge_true->probability;
return loop_version (loop, unshare_expr (cond),
- NULL, prob_true, prob_true,
+ NULL, prob_true, REG_BR_PROB_BASE - prob_true, prob_true,
REG_BR_PROB_BASE - prob_true, false);
}
@@ -439,6 +516,8 @@ tree_unswitch_outer_loop (struct loop *loop)
/* If the loop is not expected to iterate, there is no need
for unswitching. */
iterations = estimated_loop_iterations_int (loop);
+ if (iterations < 0)
+ iterations = likely_max_loop_iterations_int (loop);
if (iterations >= 0 && iterations <= 1)
{
if (dump_file && (dump_flags & TDF_DETAILS))
@@ -447,14 +526,15 @@ tree_unswitch_outer_loop (struct loop *loop)
return false;
}
- guard = find_loop_guard (loop);
- if (guard)
+ bool changed = false;
+ while ((guard = find_loop_guard (loop)))
{
+ if (! changed)
+ rewrite_virtuals_into_loop_closed_ssa (loop);
hoist_guard (loop, guard);
- update_ssa (TODO_update_ssa);
- return true;
+ changed = true;
}
- return false;
+ return changed;
}
/* Checks if the body of the LOOP is within an invariant guard. If this
@@ -497,13 +577,28 @@ find_loop_guard (struct loop *loop)
b) anything defined in something1, something2 and something3
is not used outside of the loop. */
- while (single_succ_p (header))
- header = single_succ (header);
- if (!last_stmt (header)
- || gimple_code (last_stmt (header)) != GIMPLE_COND)
- return NULL;
-
- extract_true_false_edges_from_block (header, &te, &fe);
+ gcond *cond;
+ do
+ {
+ if (single_succ_p (header))
+ header = single_succ (header);
+ else
+ {
+ cond = dyn_cast <gcond *> (last_stmt (header));
+ if (! cond)
+ return NULL;
+ extract_true_false_edges_from_block (header, &te, &fe);
+ /* Make sure to skip earlier hoisted guards that are left
+ in place as if (true). */
+ if (gimple_cond_true_p (cond))
+ header = te->dest;
+ else if (gimple_cond_false_p (cond))
+ header = fe->dest;
+ else
+ break;
+ }
+ }
+ while (1);
if (!flow_bb_inside_loop_p (loop, te->dest)
|| !flow_bb_inside_loop_p (loop, fe->dest))
return NULL;
@@ -545,7 +640,7 @@ find_loop_guard (struct loop *loop)
guard_edge->src->index, guard_edge->dest->index, loop->num);
/* Check if condition operands do not have definitions inside loop since
any bb copying is not performed. */
- FOR_EACH_SSA_TREE_OPERAND (use, last_stmt (header), iter, SSA_OP_USE)
+ FOR_EACH_SSA_TREE_OPERAND (use, cond, iter, SSA_OP_USE)
{
gimple *def = SSA_NAME_DEF_STMT (use);
basic_block def_bb = gimple_bb (def);
@@ -690,6 +785,7 @@ hoist_guard (struct loop *loop, edge guard)
edge te, fe, e, new_edge;
gimple *stmt;
basic_block guard_bb = guard->src;
+ edge not_guard;
gimple_stmt_iterator gsi;
int flags = 0;
bool fix_dom_of_exit;
@@ -721,18 +817,80 @@ hoist_guard (struct loop *loop, edge guard)
update_stmt (cond_stmt);
/* Create new loop pre-header. */
e = split_block (pre_header, last_stmt (pre_header));
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Moving guard %i->%i (prob %i) to bb %i, "
+ "new preheader is %i\n",
+ guard->src->index, guard->dest->index, guard->probability,
+ e->src->index, e->dest->index);
+
gcc_assert (loop_preheader_edge (loop)->src == e->dest);
+
if (guard == fe)
{
e->flags = EDGE_TRUE_VALUE;
flags |= EDGE_FALSE_VALUE;
+ not_guard = te;
}
else
{
e->flags = EDGE_FALSE_VALUE;
flags |= EDGE_TRUE_VALUE;
+ not_guard = fe;
}
new_edge = make_edge (pre_header, exit->dest, flags);
+
+ /* Determine the probability that we skip the loop. Assume that loop has
+ same average number of iterations regardless outcome of guard. */
+ new_edge->probability = guard->probability;
+ int skip_count = guard->src->count
+ ? RDIV (guard->count * pre_header->count, guard->src->count)
+ : apply_probability (guard->count, new_edge->probability);
+
+ if (skip_count > e->count)
+ {
+ fprintf (dump_file, " Capping count; expect profile inconsistency\n");
+ skip_count = e->count;
+ }
+ new_edge->count = skip_count;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Estimated probability of skipping loop is %i\n",
+ new_edge->probability);
+
+ /* Update profile after the transform:
+
+ First decrease count of path from newly hoisted loop guard
+ to loop header... */
+ e->count -= skip_count;
+ e->probability = REG_BR_PROB_BASE - new_edge->probability;
+ e->dest->count = e->count;
+ e->dest->frequency = EDGE_FREQUENCY (e);
+
+ /* ... now update profile to represent that original guard will be optimized
+ away ... */
+ guard->probability = 0;
+ guard->count = 0;
+ not_guard->probability = REG_BR_PROB_BASE;
+ /* This count is wrong (frequency of not_guard does not change),
+ but will be scaled later. */
+ not_guard->count = guard->src->count;
+
+ /* ... finally scale everything in the loop except for guarded basic blocks
+ where profile does not change. */
+ basic_block *body = get_loop_body (loop);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Scaling nonguarded BBs in loop:");
+ for (unsigned int i = 0; i < loop->num_nodes; i++)
+ {
+ basic_block bb = body[i];
+ if (!dominated_by_p (CDI_DOMINATORS, bb, not_guard->dest))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " %i", bb->index);
+ scale_bbs_frequencies_int (&bb, 1, e->probability, REG_BR_PROB_BASE);
+ }
+ }
+
if (fix_dom_of_exit)
set_immediate_dominator (CDI_DOMINATORS, exit->dest, pre_header);
/* Add NEW_ADGE argument for all phi in post-header block. */
@@ -758,10 +916,10 @@ hoist_guard (struct loop *loop, edge guard)
}
}
- mark_virtual_operands_for_renaming (cfun);
- update_ssa (TODO_update_ssa);
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " guard hoisted.\n");
+ fprintf (dump_file, "\n guard hoisted.\n");
+
+ free (body);
}
/* Return true if phi argument for exit edge can be used