This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH][PR tree-optimization/67816] Fix jump threading when DOM removes conditionals in jump threading path
- From: Richard Biener <richard dot guenther at gmail dot com>
- To: Jeff Law <law at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Wed, 7 Oct 2015 10:26:51 +0200
- Subject: Re: [PATCH][PR tree-optimization/67816] Fix jump threading when DOM removes conditionals in jump threading path
- Authentication-results: sourceware.org; auth=none
- References: <561482CD dot 6060202 at redhat dot com>
On Wed, Oct 7, 2015 at 4:26 AM, Jeff Law <law@redhat.com> wrote:
>
> As touched on in the BZ, we record jump threads as a list of edges to
> traverse. A jump thread may be recorded through a block which hasn't been
> optimized by DOM yet.
>
> If DOM is able to optimize a control flow statement in such a block, then it
> will remove one or more outgoing edges from the block.
>
> Removal of an edge triggers releasing the edge back to the GC system, so
> naturally bad things happen if the threader then looks at the content of
> those edges.
>
> After some instrumentation I found this was happening for both jump threads
> with joiner blocks as well as FSM jump threads. The former are actually
> more common than the latter.
>
> Given the sequencing of the recording of the jump thread, DOM optimizing the
> COND_EXPR, subsequent releasing the edge structure and finally examination
> of the jump thread to modify the CFG I saw only one good solution and one
> bad solution.
>
> The bad solution involved removing jump thread paths at the time in which
> DOM optimizes the COND_EXPR. The problem is we have to walk the entire path
> of every recorded jump thread each time DOM performs this optimization.
> Obviously not good.
>
> So instead we record the affected edge pointers into a hash table and use
> those later to prune the jump thread paths. It's a single walk over each
> recorded jump threading path. Since we're not looking at the contents of
> the edge, this works reasonably well.
>
> One more reason to push harder for jump threading to occur independently of
> DOM using Sebastian's backward walking FSM bits.
>
> Anyway, bootstrapped and regression tested on x86_64-linux-gnu. Also
> bootstrapped and testcase tested on x86_64-linux-gnu with just release
> checking enabled.
>
> Installed on the trunk.
Hmm, other passes avoid all this by not removing edges or blocks themselves
but leaving that to cfgcleanup. They simply replace the condition in
GIMPLE_CONDs
or the switch value in GIMPLE_SWITCHes and let cleanup_control_expr_graph
do the hard part of the job.
Now - I don't know how that would interact with jump threads covering
those paths,
that is, what kind of "mess" jump threading would produce with the
conditions/switches
mangled that way.
The other nice thing is that CFG cleanup properly deals with loop
structure changes.
Richard.
> Jeff
>
> diff --git a/gcc/ChangeLog b/gcc/ChangeLog
> index 732b3d1..db6f1b6 100644
> --- a/gcc/ChangeLog
> +++ b/gcc/ChangeLog
> @@ -1,3 +1,17 @@
> +2015-10-06 Jeff Law <law@redhat.com>
> +
> + PR tree-optimization/67816
> + * tree-ssa-threadupdate.h (remove_jump_threads_including): Renamed
> + from remove_jump_threads_starting_at. Accept an edge rather than
> + a basic block.
> + * tree-ssa-threadupdate.c (removed_edges): New hash table.
> + (remove_jump_threads_including): Note edges that get removed from
> + the CFG for later pruning of jump threading paths including them.
> + (thread_through_all_blocks): Remove paths which include edges that
> + have been removed.
> + * tree-ssa-dom.c (optimize_stmt): Call remove_jump_threads_including
> + on each outgoing edges when optimizing away a control statement.
> +
> 2015-10-06 Trevor Saunders <tbsaunde+gcc@tbsaunde.org>
>
> * reorg.c (emit_delay_sequence): Store list of delay slot insns
> diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
> index 4ec4743..1882fbd 100644
> --- a/gcc/testsuite/ChangeLog
> +++ b/gcc/testsuite/ChangeLog
> @@ -1,3 +1,7 @@
> +2015-10-06 Jeff Law <law@redhat.com>
> +
> + * gcc.c-torture/compile/pr67816.c: New test.
> +
> 2015-10-07 Kugan Vivekanandarajah <kuganv@linaro.org>
>
> * gcc.target/aarch64/get_lane_f16_1.c: New test.
> diff --git a/gcc/testsuite/gcc.c-torture/compile/pr67816.c
> b/gcc/testsuite/gcc.c-torture/compile/pr67816.c
> new file mode 100644
> index 0000000..c3db3a3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.c-torture/compile/pr67816.c
> @@ -0,0 +1,19 @@
> +int a, c, d, e;
> +int b[10];
> +void fn1() {
> + int i, f = 0;
> + for (;;) {
> + i = 0;
> + for (; i < a; i++)
> + if (c) {
> + if (b[i])
> + f = 1;
> + } else if (b[i])
> + f = 0;
> + if (f)
> + d++;
> + while (e)
> + ;
> + }
> +}
> +
> diff --git a/gcc/tree-ssa-dom.c b/gcc/tree-ssa-dom.c
> index c226da5..941087d 100644
> --- a/gcc/tree-ssa-dom.c
> +++ b/gcc/tree-ssa-dom.c
> @@ -1840,8 +1840,13 @@ optimize_stmt (basic_block bb, gimple_stmt_iterator
> si,
> edge taken_edge = find_taken_edge (bb, val);
> if (taken_edge)
> {
> - /* Delete threads that start at BB. */
> - remove_jump_threads_starting_at (bb);
> +
> + /* We need to remove any queued jump threads that
> + reference outgoing edges from this block. */
> + edge_iterator ei;
> + edge e;
> + FOR_EACH_EDGE (e, ei, bb->succs)
> + remove_jump_threads_including (e);
>
> /* If BB is in a loop, then removing an outgoing edge from BB
> may cause BB to move outside the loop, changes in the
> diff --git a/gcc/tree-ssa-threadupdate.c b/gcc/tree-ssa-threadupdate.c
> index 4a147bb..26b199b 100644
> --- a/gcc/tree-ssa-threadupdate.c
> +++ b/gcc/tree-ssa-threadupdate.c
> @@ -215,6 +215,18 @@ redirection_data::equal (const redirection_data *p1,
> const redirection_data *p2)
> return true;
> }
>
> +/* Rather than search all the edges in jump thread paths each time
> + DOM is able to simply if control statement, we build a hash table
> + with the deleted edges. We only care about the address of the edge,
> + not its contents. */
> +struct removed_edges : nofree_ptr_hash<edge_def>
> +{
> + static hashval_t hash (edge e) { return htab_hash_pointer (e); }
> + static bool equal (edge e1, edge e2) { return e1 == e2; }
> +};
> +
> +static hash_table<removed_edges> *removed_edges;
> +
> /* Data structure of information to pass to hash table traversal routines.
> */
> struct ssa_local_info_t
> {
> @@ -2539,35 +2551,23 @@ valid_jump_thread_path (vec<jump_thread_edge *>
> *path)
> return true;
> }
>
> -/* Remove any queued jump threads that start at BB. */
> +/* Remove any queued jump threads that include edge E.
> +
> + We don't actually remove them here, just record the edges into ax
> + hash table. That way we can do the search once per iteration of
> + DOM/VRP rather than for every case where DOM optimizes away a COND_EXPR.
> */
>
> void
> -remove_jump_threads_starting_at (basic_block bb)
> +remove_jump_threads_including (edge_def *e)
> {
> if (!paths.exists ())
> return;
>
> - for (unsigned i = 0; i < paths.length ();)
> - {
> - vec<jump_thread_edge *> *path = paths[i];
> + if (!removed_edges)
> + removed_edges = new hash_table<struct removed_edges> (17);
>
> - /* Sadly, FSM jump threads have a slightly different
> - representation than the rest of the jump threads. */
> - if ((*path)[0]->type == EDGE_FSM_THREAD
> - && (*path)[0]->e->src == bb)
> - {
> - delete_jump_thread_path (path);
> - paths.unordered_remove (i);
> - }
> - else if ((*path)[0]->type != EDGE_FSM_THREAD
> - && (*path)[0]->e->dest == bb)
> - {
> - delete_jump_thread_path (path);
> - paths.unordered_remove (i);
> - }
> - else
> - i++;
> - }
> + edge *slot = removed_edges->find_slot (e, INSERT);
> + *slot = e;
> }
>
> /* Walk through all blocks and thread incoming edges to the appropriate
> @@ -2591,11 +2591,37 @@ thread_through_all_blocks (bool
> may_peel_loop_headers)
> struct loop *loop;
>
> if (!paths.exists ())
> - return false;
> + {
> + retval = false;
> + goto out;
> + }
>
> threaded_blocks = BITMAP_ALLOC (NULL);
> memset (&thread_stats, 0, sizeof (thread_stats));
>
> + /* Remove any paths that referenced removed edges. */
> + if (removed_edges)
> + for (i = 0; i < paths.length (); )
> + {
> + unsigned int j;
> + vec<jump_thread_edge *> *path = paths[i];
> +
> + for (j = 0; j < path->length (); j++)
> + {
> + edge e = (*path)[j]->e;
> + if (removed_edges->find_slot (e, NO_INSERT))
> + break;
> + }
> +
> + if (j != path->length ())
> + {
> + delete_jump_thread_path (path);
> + paths.unordered_remove (i);
> + continue;
> + }
> + i++;
> + }
> +
> /* Jump-thread all FSM threads before other jump-threads. */
> for (i = 0; i < paths.length ();)
> {
> @@ -2778,6 +2804,9 @@ thread_through_all_blocks (bool may_peel_loop_headers)
> if (retval)
> loops_state_set (LOOPS_NEED_FIXUP);
>
> + out:
> + delete removed_edges;
> + removed_edges = NULL;
> return retval;
> }
>
> diff --git a/gcc/tree-ssa-threadupdate.h b/gcc/tree-ssa-threadupdate.h
> index 30428e8..984b6c4 100644
> --- a/gcc/tree-ssa-threadupdate.h
> +++ b/gcc/tree-ssa-threadupdate.h
> @@ -43,7 +43,7 @@ public:
> };
>
> extern void register_jump_thread (vec <class jump_thread_edge *> *);
> -extern void remove_jump_threads_starting_at (basic_block);
> +extern void remove_jump_threads_including (edge);
> extern void delete_jump_thread_path (vec <class jump_thread_edge *> *);
> extern void remove_ctrl_stmt_and_useless_edges (basic_block, basic_block);
> #endif
>