diff options
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/ssa-thread-valid.c | 39 | ||||
-rw-r--r-- | gcc/tree-ssa-threadupdate.c | 40 |
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; |