+2015-10-11 Jeff Law <law@redhat.com>
+
+ * tree-ssa-threadedge.c (fsm_find_thread_path): Moved from here into
+ tree-ssa-threadbackward.c.
+ (fsm_find_control_statement_thread_paths): Likewise.
+ (thread_through_normal_block): Break out FSM bits and move them
+ into a new function in tree-ssa-threadbackward.c. Call new function
+ instead.
+ Minimize header file usage.
+ * tree-ssa-threadbackward.h: New file.
+ * tree-ssa-threadbackward.c: Likewise.
+ * Makefile.in (OBJS): Add tree-ssa-threadbackward.o
+
2015-10-11 Uros Bizjak <ubizjak@gmail.com>
* config/alpha/alpha.h (ALPHA_ROUND): Implement using ROUND_UP macro.
tree-ssa-structalias.o \
tree-ssa-tail-merge.o \
tree-ssa-ter.o \
+ tree-ssa-threadbackward.o \
tree-ssa-threadedge.o \
tree-ssa-threadupdate.o \
tree-ssa-uncprop.o \
--- /dev/null
+/* SSA Jump Threading
+ Copyright (C) 2005-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "predict.h"
+#include "tree.h"
+#include "gimple.h"
+#include "fold-const.h"
+#include "cfgloop.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "tree-ssa-threadupdate.h"
+#include "params.h"
+#include "tree-ssa-loop.h"
+#include "cfganal.h"
+#include "tree-pass.h"
+
+static int max_threaded_paths;
+
+/* Return true if the CFG contains at least one path from START_BB to END_BB.
+ When a path is found, record in PATH the blocks from END_BB to START_BB.
+ VISITED_BBS is used to make sure we don't fall into an infinite loop. Bound
+ the recursion to basic blocks belonging to LOOP. */
+
+static bool
+fsm_find_thread_path (basic_block start_bb, basic_block end_bb,
+ vec<basic_block, va_gc> *&path,
+ hash_set<basic_block> *visited_bbs, loop_p loop)
+{
+ if (loop != start_bb->loop_father)
+ return false;
+
+ if (start_bb == end_bb)
+ {
+ vec_safe_push (path, start_bb);
+ return true;
+ }
+
+ if (!visited_bbs->add (start_bb))
+ {
+ edge e;
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, start_bb->succs)
+ if (fsm_find_thread_path (e->dest, end_bb, path, visited_bbs, loop))
+ {
+ vec_safe_push (path, start_bb);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* We trace the value of the variable EXPR back through any phi nodes looking
+ for places where it gets a constant value and save the path. Stop after
+ having recorded MAX_PATHS jump threading paths. */
+
+static void
+fsm_find_control_statement_thread_paths (tree expr,
+ hash_set<basic_block> *visited_bbs,
+ vec<basic_block, va_gc> *&path,
+ bool seen_loop_phi)
+{
+ tree var = SSA_NAME_VAR (expr);
+ gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
+ basic_block var_bb = gimple_bb (def_stmt);
+
+ if (var == NULL || var_bb == NULL)
+ return;
+
+ /* For the moment we assume that an SSA chain only contains phi nodes, and
+ eventually one of the phi arguments will be an integer constant. In the
+ future, this could be extended to also handle simple assignments of
+ arithmetic operations. */
+ if (gimple_code (def_stmt) != GIMPLE_PHI)
+ return;
+
+ /* Avoid infinite recursion. */
+ if (visited_bbs->add (var_bb))
+ return;
+
+ gphi *phi = as_a <gphi *> (def_stmt);
+ int next_path_length = 0;
+ basic_block last_bb_in_path = path->last ();
+
+ if (loop_containing_stmt (phi)->header == gimple_bb (phi))
+ {
+ /* Do not walk through more than one loop PHI node. */
+ if (seen_loop_phi)
+ return;
+ seen_loop_phi = true;
+ }
+
+ /* Following the chain of SSA_NAME definitions, we jumped from a definition in
+ LAST_BB_IN_PATH to a definition in VAR_BB. When these basic blocks are
+ different, append to PATH the blocks from LAST_BB_IN_PATH to VAR_BB. */
+ if (var_bb != last_bb_in_path)
+ {
+ edge e;
+ int e_count = 0;
+ edge_iterator ei;
+ vec<basic_block, va_gc> *next_path;
+ vec_alloc (next_path, n_basic_blocks_for_fn (cfun));
+
+ FOR_EACH_EDGE (e, ei, last_bb_in_path->preds)
+ {
+ hash_set<basic_block> *visited_bbs = new hash_set<basic_block>;
+
+ if (fsm_find_thread_path (var_bb, e->src, next_path, visited_bbs,
+ e->src->loop_father))
+ ++e_count;
+
+ delete visited_bbs;
+
+ /* If there is more than one path, stop. */
+ if (e_count > 1)
+ {
+ vec_free (next_path);
+ return;
+ }
+ }
+
+ /* Stop if we have not found a path: this could occur when the recursion
+ is stopped by one of the bounds. */
+ if (e_count == 0)
+ {
+ vec_free (next_path);
+ return;
+ }
+
+ /* Make sure we haven't already visited any of the nodes in
+ NEXT_PATH. Don't add them here to avoid pollution. */
+ for (unsigned int i = 0; i < next_path->length () - 1; i++)
+ {
+ if (visited_bbs->contains ((*next_path)[i]))
+ {
+ vec_free (next_path);
+ return;
+ }
+ }
+
+ /* Now add the nodes to VISISTED_BBS. */
+ for (unsigned int i = 0; i < next_path->length () - 1; i++)
+ visited_bbs->add ((*next_path)[i]);
+
+ /* Append all the nodes from NEXT_PATH to PATH. */
+ vec_safe_splice (path, next_path);
+ next_path_length = next_path->length ();
+ vec_free (next_path);
+ }
+
+ gcc_assert (path->last () == var_bb);
+
+ /* Iterate over the arguments of PHI. */
+ unsigned int i;
+ for (i = 0; i < gimple_phi_num_args (phi); i++)
+ {
+ tree arg = gimple_phi_arg_def (phi, i);
+ basic_block bbi = gimple_phi_arg_edge (phi, i)->src;
+
+ /* Skip edges pointing outside the current loop. */
+ if (!arg || var_bb->loop_father != bbi->loop_father)
+ continue;
+
+ if (TREE_CODE (arg) == SSA_NAME)
+ {
+ vec_safe_push (path, bbi);
+ /* Recursively follow SSA_NAMEs looking for a constant definition. */
+ fsm_find_control_statement_thread_paths (arg, visited_bbs, path,
+ seen_loop_phi);
+
+ path->pop ();
+ continue;
+ }
+
+ if (TREE_CODE (arg) != INTEGER_CST)
+ continue;
+
+ int path_length = path->length ();
+ /* A path with less than 2 basic blocks should not be jump-threaded. */
+ if (path_length < 2)
+ continue;
+
+ if (path_length > PARAM_VALUE (PARAM_MAX_FSM_THREAD_LENGTH))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "FSM jump-thread path not considered: "
+ "the number of basic blocks on the path "
+ "exceeds PARAM_MAX_FSM_THREAD_LENGTH.\n");
+ continue;
+ }
+
+ if (max_threaded_paths <= 0)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "FSM jump-thread path not considered: "
+ "the number of previously recorded FSM paths to thread "
+ "exceeds PARAM_MAX_FSM_THREAD_PATHS.\n");
+ continue;
+ }
+
+ /* Add BBI to the path. */
+ vec_safe_push (path, bbi);
+ ++path_length;
+
+ int n_insns = 0;
+ gimple_stmt_iterator gsi;
+ int j;
+ loop_p loop = (*path)[0]->loop_father;
+ bool path_crosses_loops = false;
+
+ /* Count the number of instructions on the path: as these instructions
+ will have to be duplicated, we will not record the path if there are
+ too many instructions on the path. Also check that all the blocks in
+ the path belong to a single loop. */
+ for (j = 1; j < path_length - 1; j++)
+ {
+ basic_block bb = (*path)[j];
+
+ if (bb->loop_father != loop)
+ {
+ path_crosses_loops = true;
+ break;
+ }
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ /* Do not count empty statements and labels. */
+ if (gimple_code (stmt) != GIMPLE_NOP
+ && gimple_code (stmt) != GIMPLE_LABEL
+ && !is_gimple_debug (stmt))
+ ++n_insns;
+ }
+ }
+
+ if (path_crosses_loops)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "FSM jump-thread path not considered: "
+ "the path crosses loops.\n");
+ path->pop ();
+ continue;
+ }
+
+ if (n_insns >= PARAM_VALUE (PARAM_MAX_FSM_THREAD_PATH_INSNS))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "FSM jump-thread path not considered: "
+ "the number of instructions on the path "
+ "exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n");
+ path->pop ();
+ continue;
+ }
+
+ vec<jump_thread_edge *> *jump_thread_path
+ = new vec<jump_thread_edge *> ();
+
+ /* Record the edges between the blocks in PATH. */
+ for (j = 0; j < path_length - 1; j++)
+ {
+ edge e = find_edge ((*path)[path_length - j - 1],
+ (*path)[path_length - j - 2]);
+ gcc_assert (e);
+ jump_thread_edge *x = new jump_thread_edge (e, EDGE_FSM_THREAD);
+ jump_thread_path->safe_push (x);
+ }
+
+ /* Add the edge taken when the control variable has value ARG. */
+ edge taken_edge = find_taken_edge ((*path)[0], arg);
+ jump_thread_edge *x
+ = new jump_thread_edge (taken_edge, EDGE_NO_COPY_SRC_BLOCK);
+ jump_thread_path->safe_push (x);
+
+ register_jump_thread (jump_thread_path);
+ --max_threaded_paths;
+
+ /* Remove BBI from the path. */
+ path->pop ();
+ }
+
+ /* Remove all the nodes that we added from NEXT_PATH. */
+ if (next_path_length)
+ vec_safe_truncate (path, (path->length () - next_path_length));
+}
+
+/* Search backwards from BB looking for paths where NAME (an SSA_NAME)
+ is a constant. Record such paths for jump threading.
+
+ It is assumed that BB ends with a control statement and that by
+ finding a path where NAME is a constant, we can thread the path. */
+
+void
+find_jump_threads_backwards (tree name, basic_block bb)
+{
+ vec<basic_block, va_gc> *bb_path;
+ vec_alloc (bb_path, n_basic_blocks_for_fn (cfun));
+ vec_safe_push (bb_path, bb);
+ hash_set<basic_block> *visited_bbs = new hash_set<basic_block>;
+
+ max_threaded_paths = PARAM_VALUE (PARAM_MAX_FSM_THREAD_PATHS);
+ fsm_find_control_statement_thread_paths (name, visited_bbs, bb_path, false);
+
+ delete visited_bbs;
+ vec_free (bb_path);
+}
--- /dev/null
+/* Header file for SSA dominator optimizations.
+ Copyright (C) 2013-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_TREE_SSA_THREADFSM_H
+#define GCC_TREE_SSA_THREADFSM_H
+
+extern void find_jump_threads_backwards (tree, basic_block);
+
+#endif /* GCC_TREE_SSA_THREADFSM_H */
#include "predict.h"
#include "tree.h"
#include "gimple.h"
-#include "hard-reg-set.h"
#include "ssa.h"
-#include "alias.h"
#include "fold-const.h"
-#include "flags.h"
-#include "tm_p.h"
#include "cfgloop.h"
-#include "timevar.h"
-#include "dumpfile.h"
-#include "internal-fn.h"
#include "gimple-iterator.h"
#include "tree-cfg.h"
-#include "tree-ssa-propagate.h"
#include "tree-ssa-threadupdate.h"
-#include "langhooks.h"
#include "params.h"
#include "tree-ssa-scopedtables.h"
#include "tree-ssa-threadedge.h"
-#include "tree-ssa-loop.h"
+#include "tree-ssa-threadbackward.h"
#include "tree-ssa-dom.h"
#include "builtins.h"
-#include "cfganal.h"
/* To avoid code explosion due to jump threading, we limit the
number of statements we are going to copy. This variable
return false;
}
-/* Return true if the CFG contains at least one path from START_BB to END_BB.
- When a path is found, record in PATH the blocks from END_BB to START_BB.
- VISITED_BBS is used to make sure we don't fall into an infinite loop. Bound
- the recursion to basic blocks belonging to LOOP. */
-
-static bool
-fsm_find_thread_path (basic_block start_bb, basic_block end_bb,
- vec<basic_block, va_gc> *&path,
- hash_set<basic_block> *visited_bbs, loop_p loop)
-{
- if (loop != start_bb->loop_father)
- return false;
-
- if (start_bb == end_bb)
- {
- vec_safe_push (path, start_bb);
- return true;
- }
-
- if (!visited_bbs->add (start_bb))
- {
- edge e;
- edge_iterator ei;
- FOR_EACH_EDGE (e, ei, start_bb->succs)
- if (fsm_find_thread_path (e->dest, end_bb, path, visited_bbs, loop))
- {
- vec_safe_push (path, start_bb);
- return true;
- }
- }
-
- return false;
-}
-
-static int max_threaded_paths;
-
-/* We trace the value of the variable EXPR back through any phi nodes looking
- for places where it gets a constant value and save the path. Stop after
- having recorded MAX_PATHS jump threading paths. */
-
-static void
-fsm_find_control_statement_thread_paths (tree expr,
- hash_set<basic_block> *visited_bbs,
- vec<basic_block, va_gc> *&path,
- bool seen_loop_phi)
-{
- tree var = SSA_NAME_VAR (expr);
- gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
- basic_block var_bb = gimple_bb (def_stmt);
-
- if (var == NULL || var_bb == NULL)
- return;
-
- /* For the moment we assume that an SSA chain only contains phi nodes, and
- eventually one of the phi arguments will be an integer constant. In the
- future, this could be extended to also handle simple assignments of
- arithmetic operations. */
- if (gimple_code (def_stmt) != GIMPLE_PHI)
- return;
-
- /* Avoid infinite recursion. */
- if (visited_bbs->add (var_bb))
- return;
-
- gphi *phi = as_a <gphi *> (def_stmt);
- int next_path_length = 0;
- basic_block last_bb_in_path = path->last ();
-
- if (loop_containing_stmt (phi)->header == gimple_bb (phi))
- {
- /* Do not walk through more than one loop PHI node. */
- if (seen_loop_phi)
- return;
- seen_loop_phi = true;
- }
-
- /* Following the chain of SSA_NAME definitions, we jumped from a definition in
- LAST_BB_IN_PATH to a definition in VAR_BB. When these basic blocks are
- different, append to PATH the blocks from LAST_BB_IN_PATH to VAR_BB. */
- if (var_bb != last_bb_in_path)
- {
- edge e;
- int e_count = 0;
- edge_iterator ei;
- vec<basic_block, va_gc> *next_path;
- vec_alloc (next_path, n_basic_blocks_for_fn (cfun));
-
- FOR_EACH_EDGE (e, ei, last_bb_in_path->preds)
- {
- hash_set<basic_block> *visited_bbs = new hash_set<basic_block>;
-
- if (fsm_find_thread_path (var_bb, e->src, next_path, visited_bbs,
- e->src->loop_father))
- ++e_count;
-
- delete visited_bbs;
-
- /* If there is more than one path, stop. */
- if (e_count > 1)
- {
- vec_free (next_path);
- return;
- }
- }
-
- /* Stop if we have not found a path: this could occur when the recursion
- is stopped by one of the bounds. */
- if (e_count == 0)
- {
- vec_free (next_path);
- return;
- }
-
- /* Make sure we haven't already visited any of the nodes in
- NEXT_PATH. Don't add them here to avoid pollution. */
- for (unsigned int i = 0; i < next_path->length () - 1; i++)
- {
- if (visited_bbs->contains ((*next_path)[i]))
- {
- vec_free (next_path);
- return;
- }
- }
-
- /* Now add the nodes to VISISTED_BBS. */
- for (unsigned int i = 0; i < next_path->length () - 1; i++)
- visited_bbs->add ((*next_path)[i]);
-
- /* Append all the nodes from NEXT_PATH to PATH. */
- vec_safe_splice (path, next_path);
- next_path_length = next_path->length ();
- vec_free (next_path);
- }
-
- gcc_assert (path->last () == var_bb);
-
- /* Iterate over the arguments of PHI. */
- unsigned int i;
- for (i = 0; i < gimple_phi_num_args (phi); i++)
- {
- tree arg = gimple_phi_arg_def (phi, i);
- basic_block bbi = gimple_phi_arg_edge (phi, i)->src;
-
- /* Skip edges pointing outside the current loop. */
- if (!arg || var_bb->loop_father != bbi->loop_father)
- continue;
-
- if (TREE_CODE (arg) == SSA_NAME)
- {
- vec_safe_push (path, bbi);
- /* Recursively follow SSA_NAMEs looking for a constant definition. */
- fsm_find_control_statement_thread_paths (arg, visited_bbs, path,
- seen_loop_phi);
-
- path->pop ();
- continue;
- }
-
- if (TREE_CODE (arg) != INTEGER_CST)
- continue;
-
- int path_length = path->length ();
- /* A path with less than 2 basic blocks should not be jump-threaded. */
- if (path_length < 2)
- continue;
-
- if (path_length > PARAM_VALUE (PARAM_MAX_FSM_THREAD_LENGTH))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "FSM jump-thread path not considered: "
- "the number of basic blocks on the path "
- "exceeds PARAM_MAX_FSM_THREAD_LENGTH.\n");
- continue;
- }
-
- if (max_threaded_paths <= 0)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "FSM jump-thread path not considered: "
- "the number of previously recorded FSM paths to thread "
- "exceeds PARAM_MAX_FSM_THREAD_PATHS.\n");
- continue;
- }
-
- /* Add BBI to the path. */
- vec_safe_push (path, bbi);
- ++path_length;
-
- int n_insns = 0;
- gimple_stmt_iterator gsi;
- int j;
- loop_p loop = (*path)[0]->loop_father;
- bool path_crosses_loops = false;
-
- /* Count the number of instructions on the path: as these instructions
- will have to be duplicated, we will not record the path if there are
- too many instructions on the path. Also check that all the blocks in
- the path belong to a single loop. */
- for (j = 1; j < path_length - 1; j++)
- {
- basic_block bb = (*path)[j];
-
- if (bb->loop_father != loop)
- {
- path_crosses_loops = true;
- break;
- }
-
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gimple *stmt = gsi_stmt (gsi);
- /* Do not count empty statements and labels. */
- if (gimple_code (stmt) != GIMPLE_NOP
- && gimple_code (stmt) != GIMPLE_LABEL
- && !is_gimple_debug (stmt))
- ++n_insns;
- }
- }
-
- if (path_crosses_loops)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "FSM jump-thread path not considered: "
- "the path crosses loops.\n");
- path->pop ();
- continue;
- }
-
- if (n_insns >= PARAM_VALUE (PARAM_MAX_FSM_THREAD_PATH_INSNS))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "FSM jump-thread path not considered: "
- "the number of instructions on the path "
- "exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n");
- path->pop ();
- continue;
- }
-
- vec<jump_thread_edge *> *jump_thread_path
- = new vec<jump_thread_edge *> ();
-
- /* Record the edges between the blocks in PATH. */
- for (j = 0; j < path_length - 1; j++)
- {
- edge e = find_edge ((*path)[path_length - j - 1],
- (*path)[path_length - j - 2]);
- gcc_assert (e);
- jump_thread_edge *x = new jump_thread_edge (e, EDGE_FSM_THREAD);
- jump_thread_path->safe_push (x);
- }
-
- /* Add the edge taken when the control variable has value ARG. */
- edge taken_edge = find_taken_edge ((*path)[0], arg);
- jump_thread_edge *x
- = new jump_thread_edge (taken_edge, EDGE_NO_COPY_SRC_BLOCK);
- jump_thread_path->safe_push (x);
-
- register_jump_thread (jump_thread_path);
- --max_threaded_paths;
-
- /* Remove BBI from the path. */
- path->pop ();
- }
-
- /* Remove all the nodes that we added from NEXT_PATH. */
- if (next_path_length)
- vec_safe_truncate (path, (path->length () - next_path_length));
-}
-
/* We are exiting E->src, see if E->dest ends with a conditional
jump which has a known value when reached via E.
/* When COND cannot be simplified, try to find paths from a control
statement back through the PHI nodes which would affect that control
statement. */
- vec<basic_block, va_gc> *bb_path;
- vec_alloc (bb_path, n_basic_blocks_for_fn (cfun));
- vec_safe_push (bb_path, e->dest);
- hash_set<basic_block> *visited_bbs = new hash_set<basic_block>;
-
- max_threaded_paths = PARAM_VALUE (PARAM_MAX_FSM_THREAD_PATHS);
- fsm_find_control_statement_thread_paths (cond, visited_bbs, bb_path,
- false);
-
- delete visited_bbs;
- vec_free (bb_path);
+ find_jump_threads_backwards (cond, e->dest);
}
return 0;
}