/* 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 . */ #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; /* Simple helper to get the last statement from BB, which is assumed to be a control statement. Return NULL if the last statement is not a control statement. */ static gimple * get_gimple_control_stmt (basic_block bb) { gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb); if (gsi_end_p (gsi)) return NULL; gimple *stmt = gsi_stmt (gsi); enum gimple_code code = gimple_code (stmt); if (code == GIMPLE_COND || code == GIMPLE_SWITCH || code == GIMPLE_GOTO) return stmt; return NULL; } /* 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 *&path, hash_set *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 SSA_NAME NAME 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 name, hash_set *visited_bbs, vec *&path, bool seen_loop_phi) { gimple *def_stmt = SSA_NAME_DEF_STMT (name); basic_block var_bb = gimple_bb (def_stmt); if (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 (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 *next_path; vec_alloc (next_path, n_basic_blocks_for_fn (cfun)); /* When VAR_BB == LAST_BB_IN_PATH, then the first block in the path will already be in VISITED_BBS. When they are not equal, then we must ensure that first block is accounted for to ensure we do not create bogus jump threading paths. */ visited_bbs->add ((*path)[0]); FOR_EACH_EDGE (e, ei, last_bb_in_path->preds) { hash_set *visited_bbs = new hash_set; 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 (); 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_path = new vec (); /* 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); } gimple *stmt = get_gimple_control_stmt ((*path)[0]); gcc_assert (stmt); /* We have found a constant value for ARG. For GIMPLE_SWITCH and GIMPLE_GOTO, we use it as-is. However, for a GIMPLE_COND we need to substitute, fold and simplify. */ if (gimple_code (stmt) == GIMPLE_COND) { enum tree_code cond_code = gimple_cond_code (stmt); /* We know the underyling format of the condition. */ arg = fold_binary (cond_code, boolean_type_node, arg, gimple_cond_rhs (stmt)); } /* 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 (edge e) { if (!flag_expensive_optimizations || optimize_function_for_size_p (cfun) || e->dest->loop_father != e->src->loop_father || loop_depth (e->dest->loop_father) == 0) return; gimple *stmt = get_gimple_control_stmt (e->dest); if (!stmt) return; enum gimple_code code = gimple_code (stmt); tree name = NULL; if (code == GIMPLE_SWITCH) name = gimple_switch_index (as_a (stmt)); else if (code == GIMPLE_GOTO) name = gimple_goto_dest (stmt); else if (code == GIMPLE_COND) { if (TREE_CODE (gimple_cond_lhs (stmt)) == SSA_NAME && TREE_CODE (gimple_cond_rhs (stmt)) == INTEGER_CST && (INTEGRAL_TYPE_P (TREE_TYPE (gimple_cond_lhs (stmt))) || POINTER_TYPE_P (TREE_TYPE (gimple_cond_lhs (stmt))))) name = gimple_cond_lhs (stmt); } if (!name || TREE_CODE (name) != SSA_NAME) return; vec *bb_path; vec_alloc (bb_path, n_basic_blocks_for_fn (cfun)); vec_safe_push (bb_path, e->dest); hash_set *visited_bbs = new hash_set; 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); }