aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/ssa-thread-valid.c39
-rw-r--r--gcc/tree-ssa-threadupdate.c40
2 files changed, 68 insertions, 11 deletions
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-thread-valid.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-thread-valid.c
new file mode 100644
index 0000000..7adca97
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-thread-valid.c
@@ -0,0 +1,39 @@
+// { dg-do compile }
+// { dg-options "-O2 -fgimple -fdump-statistics" }
+
+// This is a collection of threadable paths. To simplify maintenance,
+// there should only be one threadable path per function.
+
+int global;
+
+// The thread from 3->4->5 crosses loops but is allowed because it
+// never crosses the latch (BB3) and is just an early exit out of the
+// loop.
+int __GIMPLE (ssa)
+foo1 (int x)
+{
+ int D_1420;
+ int a;
+
+ __BB(2):
+ a_4 = ~x_3(D);
+ goto __BB4;
+
+ // Latch.
+ __BB(3):
+ global = a_1;
+ goto __BB4;
+
+ __BB(4,loop_header(1)):
+ a_1 = __PHI (__BB2: a_4, __BB3: 0);
+ if (a_1 != 0)
+ goto __BB3;
+ else
+ goto __BB5;
+
+ __BB(5):
+ return;
+
+}
+
+// { dg-final { scan-tree-dump "Jumps threaded\" \"foo1\" 1" "statistics" } }
diff --git a/gcc/tree-ssa-threadupdate.c b/gcc/tree-ssa-threadupdate.c
index dcabfdb..32ce1e3 100644
--- a/gcc/tree-ssa-threadupdate.c
+++ b/gcc/tree-ssa-threadupdate.c
@@ -2766,10 +2766,17 @@ bool
jt_path_registry::cancel_invalid_paths (vec<jump_thread_edge *> &path)
{
gcc_checking_assert (!path.is_empty ());
- edge taken_edge = path[path.length () - 1]->e;
- loop_p loop = taken_edge->src->loop_father;
+ edge entry = path[0]->e;
+ edge exit = path[path.length () - 1]->e;
bool seen_latch = false;
- bool path_crosses_loops = false;
+ int loops_crossed = 0;
+ bool crossed_latch = false;
+ // Use ->dest here instead of ->src to ignore the first block. The
+ // first block is allowed to be in a different loop, since it'll be
+ // redirected. See similar comment in profitable_path_p: "we don't
+ // care about that block...".
+ loop_p loop = entry->dest->loop_father;
+ loop_p curr_loop = loop;
for (unsigned int i = 0; i < path.length (); i++)
{
@@ -2784,19 +2791,30 @@ jt_path_registry::cancel_invalid_paths (vec<jump_thread_edge *> &path)
}
if (loop->latch == e->src || loop->latch == e->dest)
- seen_latch = true;
+ {
+ seen_latch = true;
+ // Like seen_latch, but excludes the first block.
+ if (e->src != entry->src)
+ crossed_latch = true;
+ }
- // The first entry represents the block with an outgoing edge
- // that we will redirect to the jump threading path. Thus we
- // don't care about that block's loop father.
- if ((i > 0 && e->src->loop_father != loop)
- || e->dest->loop_father != loop)
- path_crosses_loops = true;
+ if (e->dest->loop_father != curr_loop)
+ {
+ curr_loop = e->dest->loop_father;
+ ++loops_crossed;
+ }
if (flag_checking && !m_backedge_threads)
gcc_assert ((path[i]->e->flags & EDGE_DFS_BACK) == 0);
}
+ // If we crossed a loop into an outer loop without crossing the
+ // latch, this is just an early exit from the loop.
+ if (loops_crossed == 1
+ && !crossed_latch
+ && flow_loop_nested_p (exit->dest->loop_father, exit->src->loop_father))
+ return false;
+
if (cfun->curr_properties & PROP_loop_opts_done)
return false;
@@ -2806,7 +2824,7 @@ jt_path_registry::cancel_invalid_paths (vec<jump_thread_edge *> &path)
"would create non-empty latch");
return true;
}
- if (path_crosses_loops)
+ if (loops_crossed)
{
cancel_thread (&path, "Path crosses loops");
return true;