@@ -1742,9 +1742,9 @@ get_loop_location (struct loop *loop)
of the for or while statement, if possible. To do this, look
for the branch guarding the loop back-edge. */
- /* If this is a simple loop with an in_edge, then the loop control
- branch is typically at the end of its source. */
- desc = get_simple_loop_desc (loop);
+ /* If this is a loop with an in_edge, then the loop control branch
+ is typically at the end of its source. */
+ desc = get_loop_desc (loop, LOOP_EXIT_COMPLEX);
if (desc->in_edge)
{
FOR_BB_INSNS_REVERSE (desc->in_edge->src, insn)
@@ -460,6 +460,16 @@ struct GTY(()) niter_desc
rtx niter_expr;
};
+enum loop_exit_type
+{
+ /* Simple exit out of a loop. */
+ LOOP_EXIT_SIMPLE,
+
+ /* Simple, but slightly more complex loop exit sometimes allowed for
+ further loop analysis. See check_complex_exit_p. */
+ LOOP_EXIT_COMPLEX
+};
+
extern void iv_analysis_loop_init (struct loop *);
extern bool iv_analyze (rtx_insn *, rtx, struct rtx_iv *);
extern bool iv_analyze_result (rtx_insn *, rtx, struct rtx_iv *);
@@ -467,12 +477,30 @@ extern bool iv_analyze_expr (rtx_insn *, rtx, machine_mode,
struct rtx_iv *);
extern rtx get_iv_value (struct rtx_iv *, rtx);
extern bool biv_p (rtx_insn *, rtx);
-extern void find_simple_exit (struct loop *, struct niter_desc *);
+extern void find_loop_exit (struct loop *, struct niter_desc *,
+ enum loop_exit_type);
extern void iv_analysis_done (void);
-extern struct niter_desc *get_simple_loop_desc (struct loop *loop);
+extern struct niter_desc *get_loop_desc (struct loop *loop,
+ enum loop_exit_type);
extern void free_simple_loop_desc (struct loop *loop);
+/* Simple version of find_loop_exit. */
+
+static inline void
+find_simple_exit (struct loop *loop, struct niter_desc *desc)
+{
+ find_loop_exit (loop, desc, LOOP_EXIT_SIMPLE);
+}
+
+/* Simple version of get_loop_desc. */
+
+static inline struct niter_desc *
+get_simple_loop_desc (struct loop *loop)
+{
+ return get_loop_desc (loop, LOOP_EXIT_SIMPLE);
+}
+
static inline struct niter_desc *
simple_loop_desc (struct loop *loop)
{
@@ -290,9 +290,27 @@ iv_analysis_loop_init (struct loop *loop)
check_iv_ref_table_size ();
}
+/* Checks that def is in an immediate predecessor of the latch block. */
+
+static bool
+def_pred_latch_p (df_ref d_ref)
+{
+ basic_block bb = DF_REF_BB (d_ref);
+ edge_iterator ei;
+ edge e;
+
+ FOR_EACH_EDGE (e, ei, current_loop->latch->preds)
+ {
+ if (e->src == bb)
+ return true;
+ }
+ return false;
+}
+
/* Finds the definition of REG that dominates loop latch and stores
it to DEF. Returns false if there is not a single definition
- dominating the latch. If REG has no definition in loop, DEF
+ dominating the latch or all defs are same and they are on different
+ predecessors of loop latch. If REG has no definition in loop, DEF
is set to NULL and true is returned. */
static bool
@@ -304,15 +322,28 @@ latch_dominating_def (rtx reg, df_ref *def)
for (adef = DF_REG_DEF_CHAIN (regno); adef; adef = DF_REF_NEXT_REG (adef))
{
+ /* Initialize this to true for the very first iteration when
+ SINGLE_RD is NULL. */
+ bool def_pred_latch = true;
+
if (!bitmap_bit_p (df->blocks_to_analyze, DF_REF_BBNO (adef))
|| !bitmap_bit_p (&bb_info->out, DF_REF_ID (adef)))
continue;
- /* More than one reaching definition. */
- if (single_rd)
+ /* More than one reaching definition is ok in case definitions are
+ in predecessors of latch block and those definitions are the same.
+ Probably this could be relaxed and check for sub-dominance instead
+ predecessor. */
+ if (single_rd
+ && (!(def_pred_latch = def_pred_latch_p (adef))
+ || !rtx_equal_p( PATTERN (DF_REF_INSN (single_rd)),
+ PATTERN (DF_REF_INSN (adef)))))
return false;
- if (!just_once_each_iteration_p (current_loop, DF_REF_BB (adef)))
+ /* It's ok if def is not executed on each iteration once we have defs on
+ latch's predecessors. */
+ if (!just_once_each_iteration_p (current_loop, DF_REF_BB (adef))
+ && !def_pred_latch)
return false;
single_rd = adef;
@@ -327,10 +358,10 @@ latch_dominating_def (rtx reg, df_ref *def)
static enum iv_grd_result
iv_get_reaching_def (rtx_insn *insn, rtx reg, df_ref *def)
{
- df_ref use, adef;
+ df_ref use, adef = NULL;
basic_block def_bb, use_bb;
rtx_insn *def_insn;
- bool dom_p;
+ bool dom_p, dom_latch_p = false;
*def = NULL;
if (!simple_reg_p (reg))
@@ -345,11 +376,26 @@ iv_get_reaching_def (rtx_insn *insn, rtx reg, df_ref *def)
if (!DF_REF_CHAIN (use))
return GRD_INVARIANT;
- /* More than one reaching def. */
+ adef = DF_REF_CHAIN (use)->ref;
+ /* Having more than one reaching def is ok once all defs are in blocks which
+ are latch's predecessors. */
if (DF_REF_CHAIN (use)->next)
- return GRD_INVALID;
+ {
+ df_link* defs;
+ unsigned int def_num = 0;
- adef = DF_REF_CHAIN (use)->ref;
+ for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
+ {
+ if (!def_pred_latch_p (defs->ref))
+ return GRD_INVALID;
+ def_num++;
+ }
+ /* Make sure all preds contain definitions. */
+ if (def_num != EDGE_COUNT (current_loop->latch->preds))
+ return GRD_INVALID;
+
+ dom_latch_p = true;
+ }
/* We do not handle setting only part of the register. */
if (DF_REF_FLAGS (adef) & DF_REF_READ_WRITE)
@@ -372,8 +418,8 @@ iv_get_reaching_def (rtx_insn *insn, rtx reg, df_ref *def)
/* The definition does not dominate the use. This is still OK if
this may be a use of a biv, i.e. if the def_bb dominates loop
- latch. */
- if (just_once_each_iteration_p (current_loop, def_bb))
+ latch or all defs are in latch's predecessors. */
+ if (dom_latch_p || just_once_each_iteration_p (current_loop, def_bb))
return GRD_MAYBE_BIV;
return GRD_INVALID;
@@ -2872,11 +2918,57 @@ fail:
return;
}
-/* Checks whether E is a simple exit from LOOP and stores its description
- into DESC. */
+/* Check whether exit is not simple but still good for further analysis.
+ Looks like such loops mostly are a result of jump threading. */
+
+static bool
+check_complex_exit_p (struct loop* loop, basic_block bb)
+{
+ edge e;
+ basic_block pred, exit;
+
+ if (EDGE_COUNT (bb->preds) > 1)
+ return false;
+
+ e = EDGE_PRED (bb, 0);
+
+ pred = e->src;
+ if (EDGE_COUNT (pred->succs) != 2)
+ return false;
+
+ /* Predecessor must be tested (at least) once during any iteration. */
+ if (!dominated_by_p (CDI_DOMINATORS, loop->latch, pred))
+ return false;
+
+ if (EDGE_SUCC (pred, 0)->dest == bb)
+ exit = EDGE_SUCC (pred, 1)->dest;
+ else
+ exit = EDGE_SUCC (pred, 0)->dest;
+
+ /* Check that EXIT is really loop exit. */
+ if (flow_bb_inside_loop_p (loop, exit))
+ {
+ edge_iterator eei;
+ edge ee;
+
+ FOR_EACH_EDGE (ee, eei, exit->succs)
+ {
+ if (!flow_bb_inside_loop_p (loop, ee->dest))
+ return true;
+ }
+ }
+ return false;
+
+}
+
+/* Checks whether E is an exit from LOOP and stores its description
+ into DESC. If EXIT_TYPE is LOOP_EXIT_SIMPLE, only simple exits are
+ considered. If EXIT_TYPE is LOOP_EXIT_COMPLEX, slightly more
+ complex loop exits are considered. */
static void
-check_simple_exit (struct loop *loop, edge e, struct niter_desc *desc)
+check_loop_exit (struct loop *loop, edge e, struct niter_desc *desc,
+ enum loop_exit_type exit_type)
{
basic_block exit_bb;
rtx condition;
@@ -2891,7 +2983,9 @@ check_simple_exit (struct loop *loop, edge e, struct niter_desc *desc)
return;
/* It must be tested (at least) once during any iteration. */
- if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit_bb))
+ if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit_bb)
+ && (exit_type == LOOP_EXIT_SIMPLE
+ || !check_complex_exit_p (loop, exit_bb)))
return;
/* It must end in a simple conditional jump. */
@@ -2921,10 +3015,14 @@ check_simple_exit (struct loop *loop, edge e, struct niter_desc *desc)
iv_number_of_iterations (loop, at, condition, desc);
}
-/* Finds a simple exit of LOOP and stores its description into DESC. */
+/* Finds an exit of LOOP and stores its description into DESC. If
+ EXIT_TYPE is LOOP_EXIT_SIMPLE, only simple exits are considered.
+ If EXIT_TYPE is LOOP_EXIT_COMPLEX, slightly more complex loop exits
+ are considered. */
void
-find_simple_exit (struct loop *loop, struct niter_desc *desc)
+find_loop_exit (struct loop *loop, struct niter_desc *desc,
+ enum loop_exit_type exit_type)
{
unsigned i;
basic_block *body;
@@ -2943,7 +3041,7 @@ find_simple_exit (struct loop *loop, struct niter_desc *desc)
if (flow_bb_inside_loop_p (loop, e->dest))
continue;
- check_simple_exit (loop, e, &act);
+ check_loop_exit (loop, e, &act, exit_type);
if (!act.simple_p)
continue;
@@ -3011,11 +3109,13 @@ find_simple_exit (struct loop *loop, struct niter_desc *desc)
free (body);
}
-/* Creates a simple loop description of LOOP if it was not computed
- already. */
+/* Creates a loop description of LOOP if it was not computed already.
+ If EXIT_TYPE is LOOP_EXIT_SIMPLE, only simple exits are considered.
+ If EXIT_TYPE is LOOP_EXIT_COMPLEX, slightly more complex loop exits
+ are considered. */
struct niter_desc *
-get_simple_loop_desc (struct loop *loop)
+get_loop_desc (struct loop *loop, enum loop_exit_type exit_type)
{
struct niter_desc *desc = simple_loop_desc (loop);
@@ -3026,7 +3126,7 @@ get_simple_loop_desc (struct loop *loop)
find_simple_loop_exit. */
desc = ggc_cleared_alloc<niter_desc> ();
iv_analysis_loop_init (loop);
- find_simple_exit (loop, desc);
+ find_loop_exit (loop, desc, exit_type);
loop->simple_loop_desc = desc;
return desc;
}
new file mode 100644
@@ -0,0 +1,40 @@
+/* PR rtl-optimization/64081 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -funroll-loops -fdump-rtl-loop2_unroll" } */
+
+long token;
+long *data;
+long *arr1;
+long *arr2;
+
+int main (int argc, const char* argv[])
+{
+ unsigned long i;
+ static long pos, start, count;
+ static int dir;
+
+ pos = 0;
+ dir = 1;
+
+ for (i = 0; i < argc; i++)
+ {
+ start = pos;
+ for (count = 0; count <= 400; count++)
+ {
+ if (token == data[pos])
+ break;
+ if (dir == 1)
+ pos = arr1[pos];
+ else
+ pos = arr2[pos];
+ if (pos == -1)
+ {
+ pos = start;
+ dir = !dir;
+ }
+ }
+ }
+ return pos + dir;
+}
+
+/* { dg-final { scan-rtl-dump "loop unrolled 7 times" "loop2_unroll" } } */