summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-dom.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-dom.c')
-rw-r--r--gcc/tree-ssa-dom.c3121
1 files changed, 3121 insertions, 0 deletions
diff --git a/gcc/tree-ssa-dom.c b/gcc/tree-ssa-dom.c
new file mode 100644
index 00000000000..0daf59b47cd
--- /dev/null
+++ b/gcc/tree-ssa-dom.c
@@ -0,0 +1,3121 @@
+/* SSA Dominator optimizations for trees
+ Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Contributed by Diego Novillo <dnovillo@redhat.com>
+
+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 2, 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 COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "flags.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "output.h"
+#include "errors.h"
+#include "expr.h"
+#include "function.h"
+#include "diagnostic.h"
+#include "timevar.h"
+#include "tree-dump.h"
+#include "tree-flow.h"
+#include "domwalk.h"
+#include "real.h"
+#include "tree-pass.h"
+#include "flags.h"
+#include "langhooks.h"
+
+/* This file implements optimizations on the dominator tree. */
+
+/* Hash table with expressions made available during the renaming process.
+ When an assignment of the form X_i = EXPR is found, the statement is
+ stored in this table. If the same expression EXPR is later found on the
+ RHS of another statement, it is replaced with X_i (thus performing
+ global redundancy elimination). Similarly as we pass through conditionals
+ we record the conditional itself as having either a true or false value
+ in this table. */
+static htab_t avail_exprs;
+
+/* Structure for entries in the expression hash table.
+
+ This requires more memory for the hash table entries, but allows us
+ to avoid creating silly tree nodes and annotations for conditionals,
+ eliminates 2 global hash tables and two block local varrays.
+
+ It also allows us to reduce the number of hash table lookups we
+ have to perform in lookup_avail_expr and finally it allows us to
+ significantly reduce the number of calls into the hashing routine
+ itself. */
+struct expr_hash_elt
+{
+ /* The value (lhs) of this expression. */
+ tree lhs;
+
+ /* The expression (rhs) we want to record. */
+ tree rhs;
+
+ /* The annotation if this element corresponds to a statement. */
+ stmt_ann_t ann;
+
+ /* The hash value for RHS/ann. */
+ hashval_t hash;
+};
+
+/* Table of constant values and copies indexed by SSA name. When the
+ renaming pass finds an assignment of a constant (X_i = C) or a copy
+ assignment from another SSA variable (X_i = Y_j), it creates a mapping
+ between X_i and the RHS in this table. This mapping is used later on,
+ when renaming uses of X_i. If an assignment to X_i is found in this
+ table, instead of using X_i, we use the RHS of the statement stored in
+ this table (thus performing very simplistic copy and constant
+ propagation). */
+static varray_type const_and_copies;
+
+/* Bitmap of SSA_NAMEs known to have a nonzero value, even if we do not
+ know their exact value. */
+static bitmap nonzero_vars;
+
+/* Track whether or not we have changed the control flow graph. */
+static bool cfg_altered;
+
+/* Statistics for dominator optimizations. */
+struct opt_stats_d
+{
+ long num_stmts;
+ long num_exprs_considered;
+ long num_re;
+};
+
+/* Value range propagation record. Each time we encounter a conditional
+ of the form SSA_NAME COND CONST we create a new vrp_element to record
+ how the condition affects the possible values SSA_NAME may have.
+
+ Each record contains the condition tested (COND), and the the range of
+ values the variable may legitimately have if COND is true. Note the
+ range of values may be a smaller range than COND specifies if we have
+ recorded other ranges for this variable. Each record also contains the
+ block in which the range was recorded for invalidation purposes.
+
+ Note that the current known range is computed lazily. This allows us
+ to avoid the overhead of computing ranges which are never queried.
+
+ When we encounter a conditional, we look for records which constrain
+ the SSA_NAME used in the condition. In some cases those records allow
+ us to determine the condition's result at compile time. In other cases
+ they may allow us to simplify the condition.
+
+ We also use value ranges to do things like transform signed div/mod
+ operations into unsigned div/mod or to simplify ABS_EXPRs.
+
+ Simple experiments have shown these optimizations to not be all that
+ useful on switch statements (much to my surprise). So switch statement
+ optimizations are not performed.
+
+ Note carefully we do not propagate information through each statement
+ in the block. ie, if we know variable X has a value defined of
+ [0, 25] and we encounter Y = X + 1, we do not track a value range
+ for Y (which would be [1, 26] if we cared). Similarly we do not
+ constrain values as we encounter narrowing typecasts, etc. */
+
+struct vrp_element
+{
+ /* The highest and lowest values the variable in COND may contain when
+ COND is true. Note this may not necessarily be the same values
+ tested by COND if the same variable was used in earlier conditionals.
+
+ Note this is computed lazily and thus can be NULL indicating that
+ the values have not been computed yet. */
+ tree low;
+ tree high;
+
+ /* The actual conditional we recorded. This is needed since we compute
+ ranges lazily. */
+ tree cond;
+
+ /* The basic block where this record was created. We use this to determine
+ when to remove records. */
+ basic_block bb;
+};
+
+static struct opt_stats_d opt_stats;
+
+/* This virtual array holds pairs of edges which describe a scheduled
+ edge redirection from jump threading.
+
+ The first entry in each pair is the edge we are going to redirect.
+
+ The second entry in each pair is the edge leading to our final
+ destination block. By providing this as an edge rather than the
+ final target block itself we can correctly handle redirections
+ when the target block had PHIs which required edge insertions/splitting
+ to remove the PHIs. */
+static GTY(()) varray_type redirection_edges;
+
+/* A virtual array holding value range records for the variable identified
+ by the index, SSA_VERSION. */
+static varray_type vrp_data;
+
+/* Datastructure for block local data used during the dominator walk.
+ We maintain a stack of these as we recursively walk down the
+ dominator tree. */
+
+struct dom_walk_block_data
+{
+ /* Array of all the expressions entered into the global expression
+ hash table by this block. During finalization we use this array to
+ know what expressions to remove from the global expression hash
+ table. */
+ varray_type avail_exprs;
+
+ /* Array of dest, src pairs that need to be restored during finalization
+ into the global const/copies table during finalization. */
+ varray_type const_and_copies;
+
+ /* Similarly for the nonzero state of variables that needs to be
+ restored during finalization. */
+ varray_type nonzero_vars;
+
+ /* Array of statements we need to rescan during finalization for newly
+ exposed variables. */
+ varray_type stmts_to_rescan;
+
+ /* Array of variables which have their values constrained by operations
+ in this basic block. We use this during finalization to know
+ which variables need their VRP data updated. */
+ varray_type vrp_variables;
+
+ /* Array of tree pairs used to restore the global currdefs to its
+ original state after completing optimization of a block and its
+ dominator children. */
+ varray_type block_defs;
+};
+
+struct eq_expr_value
+{
+ tree src;
+ tree dst;
+};
+
+/* Local functions. */
+static void optimize_stmt (struct dom_walk_data *,
+ basic_block bb,
+ block_stmt_iterator);
+static inline tree get_value_for (tree, varray_type table);
+static inline void set_value_for (tree, tree, varray_type table);
+static tree lookup_avail_expr (tree, varray_type *, bool);
+static struct eq_expr_value get_eq_expr_value (tree, int, varray_type *,
+ basic_block, varray_type *);
+static hashval_t avail_expr_hash (const void *);
+static int avail_expr_eq (const void *, const void *);
+static void htab_statistics (FILE *, htab_t);
+static void record_cond (tree, tree, varray_type *);
+static void record_const_or_copy (tree, tree, varray_type *);
+static void record_equality (tree, tree, varray_type *);
+static tree update_rhs_and_lookup_avail_expr (tree, tree, varray_type *,
+ stmt_ann_t, bool);
+static tree simplify_rhs_and_lookup_avail_expr (struct dom_walk_data *,
+ tree, stmt_ann_t, int);
+static tree simplify_cond_and_lookup_avail_expr (tree, varray_type *,
+ stmt_ann_t, int);
+static tree simplify_switch_and_lookup_avail_expr (tree, varray_type *,
+ stmt_ann_t, int);
+static tree find_equivalent_equality_comparison (tree);
+static void record_range (tree, basic_block, varray_type *);
+static bool extract_range_from_cond (tree, tree *, tree *, int *);
+static void record_equivalences_from_phis (struct dom_walk_data *, basic_block);
+static void record_equivalences_from_incoming_edge (struct dom_walk_data *,
+ basic_block);
+static bool eliminate_redundant_computations (struct dom_walk_data *,
+ tree, stmt_ann_t);
+static void record_equivalences_from_stmt (tree, varray_type *, varray_type *,
+ int, stmt_ann_t);
+static void thread_across_edge (struct dom_walk_data *, edge);
+static void dom_opt_finalize_block (struct dom_walk_data *, basic_block);
+static void dom_opt_initialize_block_local_data (struct dom_walk_data *,
+ basic_block, bool);
+static void dom_opt_initialize_block (struct dom_walk_data *, basic_block);
+static void cprop_into_phis (struct dom_walk_data *, basic_block);
+static void remove_local_expressions_from_table (varray_type locals,
+ unsigned limit,
+ htab_t table);
+static void restore_vars_to_original_value (varray_type locals,
+ unsigned limit,
+ varray_type table);
+static void restore_currdefs_to_original_value (varray_type locals,
+ unsigned limit);
+static void register_definitions_for_stmt (stmt_ann_t, varray_type *);
+static void redirect_edges_and_update_ssa_graph (varray_type);
+
+/* Local version of fold that doesn't introduce cruft. */
+
+static tree
+local_fold (tree t)
+{
+ t = fold (t);
+
+ /* Strip away useless type conversions. Both the NON_LVALUE_EXPR that
+ may have been added by fold, and "useless" type conversions that might
+ now be apparent due to propagation. */
+ STRIP_MAIN_TYPE_NOPS (t);
+ STRIP_USELESS_TYPE_CONVERSION (t);
+
+ return t;
+}
+
+/* Return the value associated with variable VAR in TABLE. */
+
+static inline tree
+get_value_for (tree var, varray_type table)
+{
+ return VARRAY_TREE (table, SSA_NAME_VERSION (var));
+}
+
+/* Associate VALUE to variable VAR in TABLE. */
+
+static inline void
+set_value_for (tree var, tree value, varray_type table)
+{
+ VARRAY_TREE (table, SSA_NAME_VERSION (var)) = value;
+}
+
+/* REDIRECTION_EDGES contains edge pairs where we want to revector the
+ destination of the first edge to the destination of the second edge.
+
+ These redirections may significantly change the SSA graph since we
+ allow redirection through blocks with PHI nodes and blocks with
+ real instructions in some cases.
+
+ This routine will perform the requested redirections and incrementally
+ update the SSA graph.
+
+ Note in some cases requested redirections may be ignored as they can
+ not be safely implemented. */
+
+static void
+redirect_edges_and_update_ssa_graph (varray_type redirection_edges)
+{
+ basic_block tgt;
+ unsigned int i;
+ size_t old_num_referenced_vars = num_referenced_vars;
+
+ /* First note any variables which we are going to have to take
+ out of SSA form. */
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
+ {
+ block_stmt_iterator bsi;
+ edge e;
+ basic_block tgt;
+ tree phi;
+
+ e = VARRAY_EDGE (redirection_edges, i);
+ tgt = VARRAY_EDGE (redirection_edges, i + 1)->dest;
+
+ /* All variables referenced in PHI nodes we bypass must be
+ renamed. */
+ for (phi = phi_nodes (e->dest); phi; phi = TREE_CHAIN (phi))
+ {
+ tree result = SSA_NAME_VAR (PHI_RESULT (phi));
+ bitmap_set_bit (vars_to_rename, var_ann (result)->uid);
+ }
+
+ /* Any variables set by statements at the start of the block we
+ are bypassing must also be taken our of SSA form. */
+ for (bsi = bsi_start (e->dest); ! bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ unsigned int j;
+ def_optype defs;
+ vdef_optype vdefs;
+ tree stmt = bsi_stmt (bsi);
+ stmt_ann_t ann = stmt_ann (stmt);
+
+ if (TREE_CODE (stmt) == COND_EXPR)
+ break;
+
+ get_stmt_operands (stmt);
+
+ defs = DEF_OPS (ann);
+ for (j = 0; j < NUM_DEFS (defs); j++)
+ {
+ tree op = SSA_NAME_VAR (DEF_OP (defs, j));
+ bitmap_set_bit (vars_to_rename, var_ann (op)->uid);
+ }
+
+ vdefs = VDEF_OPS (ann);
+ for (j = 0; j < NUM_VDEFS (vdefs); j++)
+ {
+ tree op = VDEF_RESULT (vdefs, j);
+ bitmap_set_bit (vars_to_rename, var_ann (op)->uid);
+ }
+ }
+
+ /* Finally, any variables in PHI nodes at our final destination
+ must also be taken our of SSA form. */
+ for (phi = phi_nodes (tgt); phi; phi = TREE_CHAIN (phi))
+ {
+ tree result = SSA_NAME_VAR (PHI_RESULT (phi));
+ int j;
+
+ bitmap_set_bit (vars_to_rename, var_ann (result)->uid);
+
+ for (j = 0; j < PHI_NUM_ARGS (phi); j++)
+ {
+ tree arg = PHI_ARG_DEF (phi, j);
+
+ if (TREE_CODE (arg) != SSA_NAME)
+ continue;
+
+ arg = SSA_NAME_VAR (arg);
+ bitmap_set_bit (vars_to_rename, var_ann (arg)->uid);
+ }
+ }
+ }
+
+ /* Take those selected variables out of SSA form. This must be
+ done before we start redirecting edges. */
+ if (bitmap_first_set_bit (vars_to_rename) >= 0)
+ rewrite_vars_out_of_ssa (vars_to_rename);
+
+ /* The out of SSA translation above may split the edge from
+ E->src to E->dest. This could potentially cause us to lose
+ an assignment leading to invalid warnings about uninitialized
+ variables or incorrect code.
+
+ Luckily, we can detect this by looking at the last statement
+ in E->dest. If it is not a COND_EXPR or SWITCH_EXPR, then
+ the edge was split and instead of E, we want E->dest->succ. */
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
+ {
+ edge e = VARRAY_EDGE (redirection_edges, i);
+ tree last = last_stmt (e->dest);
+
+ if (last
+ && TREE_CODE (last) != COND_EXPR
+ && TREE_CODE (last) != SWITCH_EXPR)
+ {
+ e = e->dest->succ;
+
+#ifdef ENABLE_CHECKING
+ /* There should only be a single successor if the
+ original edge was split. */
+ if (e->succ_next)
+ abort ();
+#endif
+ /* Replace the edge in REDIRECTION_EDGES for the
+ loop below. */
+ VARRAY_EDGE (redirection_edges, i) = e;
+ }
+ }
+
+ /* If we created any new variables as part of the out-of-ssa
+ translation, then any jump threads must be invalidated if they
+ bypass a block in which we skipped instructions.
+
+ This is necessary as instructions which appeared to be NOPS
+ may be necessary after the out-of-ssa translation. */
+ if (num_referenced_vars != old_num_referenced_vars)
+ {
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
+ {
+ block_stmt_iterator bsi;
+ edge e;
+
+ e = VARRAY_EDGE (redirection_edges, i);
+ for (bsi = bsi_start (e->dest); ! bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree stmt = bsi_stmt (bsi);
+
+ if (IS_EMPTY_STMT (stmt)
+ || TREE_CODE (stmt) == LABEL_EXPR)
+ continue;
+
+ if (TREE_CODE (stmt) == COND_EXPR)
+ break;
+
+ /* Invalidate the jump thread. */
+ VARRAY_EDGE (redirection_edges, i) = NULL;
+ VARRAY_EDGE (redirection_edges, i + 1) = NULL;
+ break;
+ }
+ }
+ }
+
+ /* Now redirect the edges. */
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_edges); i += 2)
+ {
+ basic_block src;
+ edge e;
+
+ e = VARRAY_EDGE (redirection_edges, i);
+ if (!e)
+ continue;
+
+ tgt = VARRAY_EDGE (redirection_edges, i + 1)->dest;
+
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Threaded jump %d --> %d to %d\n",
+ e->src->index, e->dest->index, tgt->index);
+
+ src = e->src;
+
+ e = redirect_edge_and_branch (e, tgt);
+ PENDING_STMT (e) = NULL_TREE;
+
+ /* Updating the dominance information would be nontrivial. */
+ free_dominance_info (CDI_DOMINATORS);
+
+ if ((dump_file && (dump_flags & TDF_DETAILS))
+ && e->src != src)
+ fprintf (dump_file, " basic block %d created\n",
+ e->src->index);
+
+ cfg_altered = true;
+ }
+
+ VARRAY_CLEAR (redirection_edges);
+
+ for (i = old_num_referenced_vars; i < num_referenced_vars; i++)
+ {
+ bitmap_set_bit (vars_to_rename, i);
+ var_ann (referenced_var (i))->out_of_ssa_tag = 0;
+ }
+}
+
+/* Jump threading, redundancy elimination and const/copy propagation.
+
+ Optimize function FNDECL based on a walk through the dominator tree.
+
+ This pass may expose new symbols that need to be renamed into SSA. For
+ every new symbol exposed, its corresponding bit will be set in
+ VARS_TO_RENAME.
+
+ PHASE indicates which dump file from the DUMP_FILES array to use when
+ dumping debugging information. */
+
+static void
+tree_ssa_dominator_optimize (void)
+{
+ basic_block bb;
+ struct dom_walk_data walk_data;
+ unsigned int i;
+
+ for (i = 0; i < num_referenced_vars; i++)
+ var_ann (referenced_var (i))->current_def = NULL;
+
+ /* Mark loop edges so we avoid threading across loop boundaries.
+ This may result in transforming natural loop into irreducible
+ region. */
+ mark_dfs_back_edges ();
+
+ /* Create our hash tables. */
+ avail_exprs = htab_create (1024, avail_expr_hash, avail_expr_eq, free);
+ VARRAY_TREE_INIT (const_and_copies, highest_ssa_version, "const_and_copies");
+ nonzero_vars = BITMAP_XMALLOC ();
+ VARRAY_EDGE_INIT (redirection_edges, 20, "redirection_edges");
+ VARRAY_GENERIC_PTR_INIT (vrp_data, highest_ssa_version, "vrp_data");
+
+ /* Setup callbacks for the generic dominator tree walker. */
+ walk_data.walk_stmts_backward = false;
+ walk_data.dom_direction = CDI_DOMINATORS;
+ walk_data.initialize_block_local_data = dom_opt_initialize_block_local_data;
+ walk_data.before_dom_children_before_stmts = dom_opt_initialize_block;
+ walk_data.before_dom_children_walk_stmts = optimize_stmt;
+ walk_data.before_dom_children_after_stmts = cprop_into_phis;
+ walk_data.after_dom_children_before_stmts = NULL;
+ walk_data.after_dom_children_walk_stmts = NULL;
+ walk_data.after_dom_children_after_stmts = dom_opt_finalize_block;
+ /* Right now we only attach a dummy COND_EXPR to the global data pointer.
+ When we attach more stuff we'll need to fill this out with a real
+ structure. */
+ walk_data.global_data = NULL;
+ walk_data.block_local_data_size = sizeof (struct dom_walk_block_data);
+
+ /* Now initialize the dominator walker. */
+ init_walk_dominator_tree (&walk_data);
+
+ /* Reset block_forwardable in each block's annotation. We use that
+ attribute when threading through COND_EXPRs. */
+ FOR_EACH_BB (bb)
+ bb_ann (bb)->forwardable = 1;
+
+ calculate_dominance_info (CDI_DOMINATORS);
+
+ /* If we prove certain blocks are unreachable, then we want to
+ repeat the dominator optimization process as PHI nodes may
+ have turned into copies which allows better propagation of
+ values. So we repeat until we do not identify any new unreachable
+ blocks. */
+ do
+ {
+ /* Optimize the dominator tree. */
+ cfg_altered = false;
+
+ /* Recursively walk the dominator tree optimizing statements. */
+ walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR);
+
+ /* Wipe the hash tables. */
+
+ if (VARRAY_ACTIVE_SIZE (redirection_edges) > 0)
+ redirect_edges_and_update_ssa_graph (redirection_edges);
+
+ /* We may have made some basic blocks unreachable, remove them. */
+ cfg_altered |= delete_unreachable_blocks ();
+
+ /* If the CFG was altered, then recompute the dominator tree. This
+ is not strictly needed if we only removed unreachable blocks, but
+ may produce better results. If we threaded jumps, then rebuilding
+ the dominator tree is strictly necessary. */
+ if (cfg_altered)
+ {
+ cleanup_tree_cfg ();
+ calculate_dominance_info (CDI_DOMINATORS);
+ }
+
+ /* If we are going to iterate (CFG_ALTERED is true), then we must
+ perform any queued renaming before the next iteration. */
+ if (cfg_altered
+ && bitmap_first_set_bit (vars_to_rename) >= 0)
+ {
+ rewrite_into_ssa ();
+ bitmap_clear (vars_to_rename);
+
+ /* The into SSA translation may have created new SSA_NAMES whic
+ affect the size of CONST_AND_COPIES and VRP_DATA. */
+ VARRAY_GROW (const_and_copies, highest_ssa_version);
+ VARRAY_GROW (vrp_data, highest_ssa_version);
+ }
+
+ /* Reinitialize the various tables. */
+ bitmap_clear (nonzero_vars);
+ htab_empty (avail_exprs);
+ VARRAY_CLEAR (const_and_copies);
+ VARRAY_CLEAR (vrp_data);
+
+ for (i = 0; i < num_referenced_vars; i++)
+ var_ann (referenced_var (i))->current_def = NULL;
+ }
+ while (cfg_altered);
+
+ /* Remove any unreachable blocks left behind and linearize the CFG. */
+ cleanup_tree_cfg ();
+
+ /* Debugging dumps. */
+ if (dump_file && (dump_flags & TDF_STATS))
+ dump_dominator_optimization_stats (dump_file);
+
+ /* We emptyed the hash table earlier, now delete it completely. */
+ htab_delete (avail_exprs);
+
+ /* It is not nocessary to clear CURRDEFS, REDIRECTION_EDGES, VRP_DATA,
+ CONST_AND_COPIES, and NONZERO_VARS as they all get cleared at the bottom
+ of the do-while loop above. */
+
+ /* And finalize the dominator walker. */
+ fini_walk_dominator_tree (&walk_data);
+}
+
+static bool
+gate_dominator (void)
+{
+ return flag_tree_dom != 0;
+}
+
+struct tree_opt_pass pass_dominator =
+{
+ "dom", /* name */
+ gate_dominator, /* gate */
+ tree_ssa_dominator_optimize, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_SSA_DOMINATOR_OPTS, /* tv_id */
+ PROP_cfg | PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func | TODO_rename_vars
+ | TODO_verify_ssa /* todo_flags_finish */
+};
+
+
+/* We are exiting BB, see if the target block begins with a conditional
+ jump which has a known value when reached via BB. */
+
+static void
+thread_across_edge (struct dom_walk_data *walk_data, edge e)
+{
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+ block_stmt_iterator bsi;
+ tree stmt = NULL;
+ tree phi;
+
+ /* Each PHI creates a temporary equivalence, record them. */
+ for (phi = phi_nodes (e->dest); phi; phi = TREE_CHAIN (phi))
+ {
+ tree src = PHI_ARG_DEF (phi, phi_arg_from_edge (phi, e));
+ tree dst = PHI_RESULT (phi);
+ record_const_or_copy (dst, src, &bd->const_and_copies);
+ register_new_def (dst, &bd->block_defs);
+ }
+
+ for (bsi = bsi_start (e->dest); ! bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree lhs, cached_lhs;
+
+ stmt = bsi_stmt (bsi);
+
+ /* Ignore empty statements and labels. */
+ if (IS_EMPTY_STMT (stmt) || TREE_CODE (stmt) == LABEL_EXPR)
+ continue;
+
+ /* If this is not a MODIFY_EXPR which sets an SSA_NAME to a new
+ value, then stop our search here. Ideally when we stop a
+ search we stop on a COND_EXPR or SWITCH_EXPR. */
+ if (TREE_CODE (stmt) != MODIFY_EXPR
+ || TREE_CODE (TREE_OPERAND (stmt, 0)) != SSA_NAME)
+ break;
+
+ /* At this point we have a statement which assigns an RHS to an
+ SSA_VAR on the LHS. We want to prove that the RHS is already
+ available and that its value is held in the current definition
+ of the LHS -- meaning that this assignment is a NOP when
+ reached via edge E. */
+ if (TREE_CODE (TREE_OPERAND (stmt, 1)) == SSA_NAME)
+ cached_lhs = TREE_OPERAND (stmt, 1);
+ else
+ cached_lhs = lookup_avail_expr (stmt, NULL, false);
+
+ lhs = TREE_OPERAND (stmt, 0);
+
+ /* This can happen if we thread around to the start of a loop. */
+ if (lhs == cached_lhs)
+ break;
+
+ /* If we did not find RHS in the hash table, then try again after
+ temporarily const/copy propagating the operands. */
+ if (!cached_lhs)
+ {
+ /* Copy the operands. */
+ stmt_ann_t ann = stmt_ann (stmt);
+ use_optype uses = USE_OPS (ann);
+ vuse_optype vuses = VUSE_OPS (ann);
+ tree *uses_copy = xcalloc (NUM_USES (uses), sizeof (tree));
+ tree *vuses_copy = xcalloc (NUM_VUSES (vuses), sizeof (tree));
+ unsigned int i;
+
+ /* Make a copy of the uses into USES_COPY, then cprop into
+ the use operands. */
+ for (i = 0; i < NUM_USES (uses); i++)
+ {
+ tree tmp = NULL;
+
+ uses_copy[i] = USE_OP (uses, i);
+ if (TREE_CODE (USE_OP (uses, i)) == SSA_NAME)
+ tmp = get_value_for (USE_OP (uses, i), const_and_copies);
+ if (tmp)
+ *USE_OP_PTR (uses, i) = tmp;
+ }
+
+ /* Similarly for virtual uses. */
+ for (i = 0; i < NUM_VUSES (vuses); i++)
+ {
+ tree tmp = NULL;
+
+ vuses_copy[i] = VUSE_OP (vuses, i);
+ if (TREE_CODE (VUSE_OP (vuses, i)) == SSA_NAME)
+ tmp = get_value_for (VUSE_OP (vuses, i), const_and_copies);
+ if (tmp)
+ VUSE_OP (vuses, i) = tmp;
+ }
+
+ /* Try to lookup the new expression. */
+ cached_lhs = lookup_avail_expr (stmt, NULL, false);
+
+ /* Restore the statement's original uses/defs. */
+ for (i = 0; i < NUM_USES (uses); i++)
+ *USE_OP_PTR (uses, i) = uses_copy[i];
+
+ for (i = 0; i < NUM_VUSES (vuses); i++)
+ VUSE_OP (vuses, i) = vuses_copy[i];
+
+ free (uses_copy);
+ free (vuses_copy);
+
+ /* If we still did not find the expression in the hash table,
+ then we can not ignore this statement. */
+ if (! cached_lhs)
+ break;
+ }
+
+ /* If the expression in the hash table was not assigned to an
+ SSA_NAME, then we can not ignore this statement. */
+ if (TREE_CODE (cached_lhs) != SSA_NAME)
+ break;
+
+ /* If we have different underlying variables, then we can not
+ ignore this statement. */
+ if (SSA_NAME_VAR (cached_lhs) != SSA_NAME_VAR (lhs))
+ break;
+
+ /* If CACHED_LHS does not represent the current value of the undering
+ variable in CACHED_LHS/LHS, then we can not ignore this statement. */
+ if (var_ann (SSA_NAME_VAR (lhs))->current_def != cached_lhs)
+ break;
+
+ /* If we got here, then we can ignore this statement and continue
+ walking through the statements in the block looking for a threadable
+ COND_EXPR.
+
+ We want to record an equivalence lhs = cache_lhs so that if
+ the result of this statement is used later we can copy propagate
+ suitably. */
+ record_const_or_copy (lhs, cached_lhs, &bd->const_and_copies);
+ register_new_def (lhs, &bd->block_defs);
+ }
+
+ /* If we stopped at a COND_EXPR or SWITCH_EXPR, then see if we know which
+ arm will be taken. */
+ if (stmt
+ && (TREE_CODE (stmt) == COND_EXPR
+ || TREE_CODE (stmt) == SWITCH_EXPR))
+ {
+ tree cond, cached_lhs;
+ edge e1;
+
+ /* Do not forward entry edges into the loop. In the case loop
+ has multiple entry edges we may end up in constructing irreducible
+ region.
+ ??? We may consider forwarding the edges in the case all incoming
+ edges forward to the same destination block. */
+ if (!e->flags & EDGE_DFS_BACK)
+ {
+ for (e1 = e->dest->pred; e; e = e->pred_next)
+ if (e1->flags & EDGE_DFS_BACK)
+ break;
+ if (e1)
+ return;
+ }
+
+ /* Now temporarily cprop the operands and try to find the resulting
+ expression in the hash tables. */
+ if (TREE_CODE (stmt) == COND_EXPR)
+ cond = COND_EXPR_COND (stmt);
+ else
+ cond = SWITCH_COND (stmt);
+
+ if (TREE_CODE_CLASS (TREE_CODE (cond)) == '<')
+ {
+ tree dummy_cond, op0, op1;
+ enum tree_code cond_code;
+
+ op0 = TREE_OPERAND (cond, 0);
+ op1 = TREE_OPERAND (cond, 1);
+ cond_code = TREE_CODE (cond);
+
+ /* Get the current value of both operands. */
+ if (TREE_CODE (op0) == SSA_NAME)
+ {
+ tree tmp = get_value_for (op0, const_and_copies);
+ if (tmp)
+ op0 = tmp;
+ }
+
+ if (TREE_CODE (op1) == SSA_NAME)
+ {
+ tree tmp = get_value_for (op1, const_and_copies);
+ if (tmp)
+ op1 = tmp;
+ }
+
+ /* Stuff the operator and operands into our dummy conditional
+ expression, creating the dummy conditional if necessary. */
+ dummy_cond = walk_data->global_data;
+ if (! dummy_cond)
+ {
+ dummy_cond = build (cond_code, boolean_type_node, op0, op1);
+ dummy_cond = build (COND_EXPR, void_type_node,
+ dummy_cond, NULL, NULL);
+ walk_data->global_data = dummy_cond;
+ }
+ else
+ {
+ TREE_SET_CODE (TREE_OPERAND (dummy_cond, 0), cond_code);
+ TREE_OPERAND (TREE_OPERAND (dummy_cond, 0), 0) = op0;
+ TREE_OPERAND (TREE_OPERAND (dummy_cond, 0), 1) = op1;
+ }
+
+ /* If the conditional folds to an invariant, then we are done,
+ otherwise look it up in the hash tables. */
+ cached_lhs = local_fold (COND_EXPR_COND (dummy_cond));
+ if (! is_gimple_min_invariant (cached_lhs))
+ cached_lhs = lookup_avail_expr (dummy_cond, NULL, false);
+ if (!cached_lhs || ! is_gimple_min_invariant (cached_lhs))
+ {
+ stmt_ann_t ann = get_stmt_ann (dummy_cond);
+ cached_lhs = simplify_cond_and_lookup_avail_expr (dummy_cond,
+ NULL,
+ ann,
+ false);
+ }
+ }
+ /* We can have conditionals which just test the state of a
+ variable rather than use a relational operator. These are
+ simpler to handle. */
+ else if (TREE_CODE (cond) == SSA_NAME)
+ {
+ cached_lhs = cond;
+ cached_lhs = get_value_for (cached_lhs, const_and_copies);
+ if (cached_lhs && ! is_gimple_min_invariant (cached_lhs))
+ cached_lhs = 0;
+ }
+ else
+ cached_lhs = lookup_avail_expr (stmt, NULL, false);
+
+ if (cached_lhs)
+ {
+ edge taken_edge = find_taken_edge (e->dest, cached_lhs);
+ basic_block dest = (taken_edge ? taken_edge->dest : NULL);
+
+ if (dest == e->src)
+ return;
+
+ /* If we have a known destination for the conditional, then
+ we can perform this optimization, which saves at least one
+ conditional jump each time it applies since we get to
+ bypass the conditional at our original destination.
+
+ Note that we can either thread through a block with PHIs
+ or to a block with PHIs, but not both. At this time the
+ bookkeeping to keep the CFG & SSA up-to-date has proven
+ difficult. */
+ if (dest)
+ {
+ int saved_forwardable = bb_ann (e->src)->forwardable;
+ edge tmp_edge;
+
+ bb_ann (e->src)->forwardable = 0;
+ tmp_edge = tree_block_forwards_to (dest);
+ taken_edge = (tmp_edge ? tmp_edge : taken_edge);
+ bb_ann (e->src)->forwardable = saved_forwardable;
+ VARRAY_PUSH_EDGE (redirection_edges, e);
+ VARRAY_PUSH_EDGE (redirection_edges, taken_edge);
+ }
+ }
+ }
+}
+
+
+/* Initialize the local stacks.
+
+ AVAIL_EXPRS stores all the expressions made available in this block.
+
+ CONST_AND_COPIES stores var/value pairs to restore at the end of this
+ block.
+
+ NONZERO_VARS stores the vars which have a nonzero value made in this
+ block.
+
+ STMTS_TO_RESCAN is a list of statements we will rescan for operands.
+
+ VRP_VARIABLES is the list of variables which have had their values
+ constrained by an operation in this block.
+
+ These stacks are cleared in the finalization routine run for each
+ block. */
+
+static void
+dom_opt_initialize_block_local_data (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
+ basic_block bb ATTRIBUTE_UNUSED,
+ bool recycled ATTRIBUTE_UNUSED)
+{
+#ifdef ENABLE_CHECKING
+ struct dom_walk_block_data *bd
+ = (struct dom_walk_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+
+ /* We get cleared memory from the allocator, so if the memory is not
+ cleared, then we are re-using a previously allocated entry. In
+ that case, we can also re-use the underlying virtual arrays. Just
+ make sure we clear them before using them! */
+ if (recycled)
+ {
+ if (bd->avail_exprs && VARRAY_ACTIVE_SIZE (bd->avail_exprs) > 0)
+ abort ();
+ if (bd->const_and_copies && VARRAY_ACTIVE_SIZE (bd->const_and_copies) > 0)
+ abort ();
+ if (bd->nonzero_vars && VARRAY_ACTIVE_SIZE (bd->nonzero_vars) > 0)
+ abort ();
+ if (bd->stmts_to_rescan && VARRAY_ACTIVE_SIZE (bd->stmts_to_rescan) > 0)
+ abort ();
+ if (bd->vrp_variables && VARRAY_ACTIVE_SIZE (bd->vrp_variables) > 0)
+ abort ();
+ if (bd->block_defs && VARRAY_ACTIVE_SIZE (bd->block_defs) > 0)
+ abort ();
+ }
+#endif
+}
+
+/* Initialize local stacks for this optimizer and record equivalences
+ upon entry to BB. Equivalences can come from the edge traversed to
+ reach BB or they may come from PHI nodes at the start of BB. */
+
+static void
+dom_opt_initialize_block (struct dom_walk_data *walk_data, basic_block bb)
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\n\nOptimizing block #%d\n\n", bb->index);
+
+ record_equivalences_from_incoming_edge (walk_data, bb);
+
+ /* PHI nodes can create equivalences too. */
+ record_equivalences_from_phis (walk_data, bb);
+}
+
+/* Given an expression EXPR (a relational expression or a statement),
+ initialize the hash table element pointed by by ELEMENT. */
+
+static void
+initialize_hash_element (tree expr, tree lhs, struct expr_hash_elt *element)
+{
+ /* Hash table elements may be based on conditional expressions or statements.
+
+ For the former case, we have no annotation and we want to hash the
+ conditional expression. In the latter case we have an annotation and
+ we want to record the expression the statement evaluates. */
+ if (TREE_CODE_CLASS (TREE_CODE (expr)) == '<'
+ || TREE_CODE (expr) == TRUTH_NOT_EXPR)
+ {
+ element->ann = NULL;
+ element->rhs = expr;
+ }
+ else if (TREE_CODE (expr) == COND_EXPR)
+ {
+ element->ann = stmt_ann (expr);
+ element->rhs = COND_EXPR_COND (expr);
+ }
+ else if (TREE_CODE (expr) == SWITCH_EXPR)
+ {
+ element->ann = stmt_ann (expr);
+ element->rhs = SWITCH_COND (expr);
+ }
+ else if (TREE_CODE (expr) == RETURN_EXPR && TREE_OPERAND (expr, 0))
+ {
+ element->ann = stmt_ann (expr);
+ element->rhs = TREE_OPERAND (TREE_OPERAND (expr, 0), 1);
+ }
+ else
+ {
+ element->ann = stmt_ann (expr);
+ element->rhs = TREE_OPERAND (expr, 1);
+ }
+
+ element->lhs = lhs;
+ element->hash = avail_expr_hash (element);
+}
+
+/* Remove all the expressions in LOCALS from TABLE, stopping when there are
+ LIMIT entries left in LOCALs. */
+
+static void
+remove_local_expressions_from_table (varray_type locals,
+ unsigned limit,
+ htab_t table)
+{
+ if (! locals)
+ return;
+
+ /* Remove all the expressions made available in this block. */
+ while (VARRAY_ACTIVE_SIZE (locals) > limit)
+ {
+ struct expr_hash_elt element;
+ tree expr = VARRAY_TOP_TREE (locals);
+ VARRAY_POP (locals);
+
+ initialize_hash_element (expr, NULL, &element);
+ htab_remove_elt_with_hash (table, &element, element.hash);
+ }
+}
+
+/* Use the SSA_NAMES in LOCALS to restore TABLE to its original
+ state, stopping when there are LIMIT entires left in LOCALs. */
+
+static void
+restore_nonzero_vars_to_original_value (varray_type locals,
+ unsigned limit,
+ bitmap table)
+{
+ if (!locals)
+ return;
+
+ while (VARRAY_ACTIVE_SIZE (locals) > limit)
+ {
+ tree name = VARRAY_TOP_TREE (locals);
+ VARRAY_POP (locals);
+ bitmap_clear_bit (table, SSA_NAME_VERSION (name));
+ }
+}
+
+/* Use the source/dest pairs in LOCALS to restore TABLE to its original
+ state, stopping when there are LIMIT entires left in LOCALs. */
+
+static void
+restore_vars_to_original_value (varray_type locals,
+ unsigned limit,
+ varray_type table)
+{
+ if (! locals)
+ return;
+
+ while (VARRAY_ACTIVE_SIZE (locals) > limit)
+ {
+ tree prev_value, dest;
+
+ prev_value = VARRAY_TOP_TREE (locals);
+ VARRAY_POP (locals);
+ dest = VARRAY_TOP_TREE (locals);
+ VARRAY_POP (locals);
+
+ set_value_for (dest, prev_value, table);
+ }
+}
+
+/* Similar to restore_vars_to_original_value, except that it restores
+ CURRDEFS to its original value. */
+static void
+restore_currdefs_to_original_value (varray_type locals, unsigned limit)
+{
+ if (!locals)
+ return;
+
+ /* Restore CURRDEFS to its original state. */
+ while (VARRAY_ACTIVE_SIZE (locals) > limit)
+ {
+ tree tmp = VARRAY_TOP_TREE (locals);
+ tree saved_def, var;
+
+ VARRAY_POP (locals);
+
+ /* If we recorded an SSA_NAME, then make the SSA_NAME the current
+ definition of its underlying variable. If we recorded anything
+ else, it must have been an _DECL node and its current reaching
+ definition must have been NULL. */
+ if (TREE_CODE (tmp) == SSA_NAME)
+ {
+ saved_def = tmp;
+ var = SSA_NAME_VAR (saved_def);
+ }
+ else
+ {
+ saved_def = NULL;
+ var = tmp;
+ }
+
+ var_ann (var)->current_def = saved_def;
+ }
+}
+
+/* We have finished processing the dominator children of BB, perform
+ any finalization actions in preparation for leaving this node in
+ the dominator tree. */
+
+static void
+dom_opt_finalize_block (struct dom_walk_data *walk_data, basic_block bb)
+{
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+ tree last;
+
+ /* If we are at a leaf node in the dominator graph, see if we can thread
+ the edge from BB through its successor.
+
+ Do this before we remove entries from our equivalence tables. */
+ if (bb->succ
+ && ! bb->succ->succ_next
+ && (bb->succ->flags & EDGE_ABNORMAL) == 0
+ && (get_immediate_dominator (CDI_DOMINATORS, bb->succ->dest) != bb
+ || phi_nodes (bb->succ->dest)))
+
+ {
+ thread_across_edge (walk_data, bb->succ);
+ }
+ else if ((last = last_stmt (bb))
+ && TREE_CODE (last) == COND_EXPR
+ && (TREE_CODE_CLASS (TREE_CODE (COND_EXPR_COND (last))) == '<'
+ || TREE_CODE (COND_EXPR_COND (last)) == SSA_NAME)
+ && bb->succ
+ && (bb->succ->flags & EDGE_ABNORMAL) == 0
+ && bb->succ->succ_next
+ && (bb->succ->succ_next->flags & EDGE_ABNORMAL) == 0
+ && ! bb->succ->succ_next->succ_next)
+ {
+ edge true_edge, false_edge;
+ tree cond, inverted = NULL;
+ enum tree_code cond_code;
+
+ extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
+
+ cond = COND_EXPR_COND (last);
+ cond_code = TREE_CODE (cond);
+
+ if (TREE_CODE_CLASS (cond_code) == '<')
+ inverted = invert_truthvalue (cond);
+
+ /* If the THEN arm is the end of a dominator tree or has PHI nodes,
+ then try to thread through its edge. */
+ if (get_immediate_dominator (CDI_DOMINATORS, true_edge->dest) != bb
+ || phi_nodes (true_edge->dest))
+ {
+ unsigned avail_expr_limit;
+ unsigned const_and_copies_limit;
+ unsigned currdefs_limit;
+
+ avail_expr_limit
+ = bd->avail_exprs ? VARRAY_ACTIVE_SIZE (bd->avail_exprs) : 0;
+ const_and_copies_limit
+ = bd->const_and_copies ? VARRAY_ACTIVE_SIZE (bd->const_and_copies)
+ : 0;
+ currdefs_limit
+ = bd->block_defs ? VARRAY_ACTIVE_SIZE (bd->block_defs) : 0;
+
+ /* Record any equivalences created by following this edge. */
+ if (TREE_CODE_CLASS (cond_code) == '<')
+ {
+ record_cond (cond, boolean_true_node, &bd->avail_exprs);
+ record_cond (inverted, boolean_false_node, &bd->avail_exprs);
+ }
+ else if (cond_code == SSA_NAME)
+ record_const_or_copy (cond, boolean_true_node,
+ &bd->const_and_copies);
+
+ /* Now thread the edge. */
+ thread_across_edge (walk_data, true_edge);
+
+ /* And restore the various tables to their state before
+ we threaded this edge. */
+ remove_local_expressions_from_table (bd->avail_exprs,
+ avail_expr_limit,
+ avail_exprs);
+ restore_vars_to_original_value (bd->const_and_copies,
+ const_and_copies_limit,
+ const_and_copies);
+ restore_currdefs_to_original_value (bd->block_defs, currdefs_limit);
+ }
+
+ /* Similarly for the ELSE arm. */
+ if (get_immediate_dominator (CDI_DOMINATORS, false_edge->dest) != bb
+ || phi_nodes (false_edge->dest))
+ {
+ /* Record any equivalences created by following this edge. */
+ if (TREE_CODE_CLASS (cond_code) == '<')
+ {
+ record_cond (cond, boolean_false_node, &bd->avail_exprs);
+ record_cond (inverted, boolean_true_node, &bd->avail_exprs);
+ }
+ else if (cond_code == SSA_NAME)
+ record_const_or_copy (cond, boolean_false_node,
+ &bd->const_and_copies);
+
+ thread_across_edge (walk_data, false_edge);
+
+ /* No need to remove local expressions from our tables
+ or restore vars to their original value as that will
+ be done immediately below. */
+ }
+ }
+
+ remove_local_expressions_from_table (bd->avail_exprs, 0, avail_exprs);
+ restore_nonzero_vars_to_original_value (bd->nonzero_vars, 0, nonzero_vars);
+ restore_vars_to_original_value (bd->const_and_copies, 0, const_and_copies);
+ restore_currdefs_to_original_value (bd->block_defs, 0);
+
+ /* Remove VRP records associated with this basic block. They are no
+ longer valid.
+
+ To be efficient, we note which variables have had their values
+ constrained in this block. So walk over each variable in the
+ VRP_VARIABLEs array. */
+ while (bd->vrp_variables && VARRAY_ACTIVE_SIZE (bd->vrp_variables) > 0)
+ {
+ tree var = VARRAY_TOP_TREE (bd->vrp_variables);
+
+ /* Each variable has a stack of value range records. We want to
+ invalidate those associated with our basic block. So we walk
+ the array backwards popping off records associated with our
+ block. Once we hit a record not associated with our block
+ we are done. */
+ varray_type var_vrp_records = VARRAY_GENERIC_PTR (vrp_data,
+ SSA_NAME_VERSION (var));
+
+ while (VARRAY_ACTIVE_SIZE (var_vrp_records) > 0)
+ {
+ struct vrp_element *element
+ = (struct vrp_element *)VARRAY_TOP_GENERIC_PTR (var_vrp_records);
+
+ if (element->bb != bb)
+ break;
+
+ VARRAY_POP (var_vrp_records);
+ }
+
+ VARRAY_POP (bd->vrp_variables);
+ }
+
+ /* Re-scan operands in all statements that may have had new symbols
+ exposed. */
+ while (bd->stmts_to_rescan && VARRAY_ACTIVE_SIZE (bd->stmts_to_rescan) > 0)
+ {
+ tree stmt = VARRAY_TOP_TREE (bd->stmts_to_rescan);
+ VARRAY_POP (bd->stmts_to_rescan);
+ mark_new_vars_to_rename (stmt, vars_to_rename);
+ }
+}
+
+/* PHI nodes can create equivalences too.
+
+ Ignoring any alternatives which are the same as the result, if
+ all the alternatives are equal, then the PHI node creates an
+ equivalence. */
+static void
+record_equivalences_from_phis (struct dom_walk_data *walk_data, basic_block bb)
+{
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+ tree phi;
+
+ for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi))
+ {
+ tree lhs = PHI_RESULT (phi);
+ tree rhs = NULL;
+ int i;
+
+ for (i = 0; i < PHI_NUM_ARGS (phi); i++)
+ {
+ tree t = PHI_ARG_DEF (phi, i);
+
+ if (TREE_CODE (t) == SSA_NAME || is_gimple_min_invariant (t))
+ {
+ /* Ignore alternatives which are the same as our LHS. */
+ if (operand_equal_p (lhs, t, 0))
+ continue;
+
+ /* If we have not processed an alternative yet, then set
+ RHS to this alternative. */
+ if (rhs == NULL)
+ rhs = t;
+ /* If we have processed an alternative (stored in RHS), then
+ see if it is equal to this one. If it isn't, then stop
+ the search. */
+ else if (! operand_equal_p (rhs, t, 0))
+ break;
+ }
+ else
+ break;
+ }
+
+ /* If we had no interesting alternatives, then all the RHS alternatives
+ must have been the same as LHS. */
+ if (!rhs)
+ rhs = lhs;
+
+ /* If we managed to iterate through each PHI alternative without
+ breaking out of the loop, then we have a PHI which may create
+ a useful equivalence. We do not need to record unwind data for
+ this, since this is a true assignment and not an equivalence
+ infered from a comparison. All uses of this ssa name are dominated
+ by this assignment, so unwinding just costs time and space. */
+ if (i == PHI_NUM_ARGS (phi)
+ && may_propagate_copy (lhs, rhs))
+ set_value_for (lhs, rhs, const_and_copies);
+
+ register_new_def (lhs, &bd->block_defs);
+ }
+}
+
+/* Record any equivalences created by the incoming edge to BB. If BB
+ has more than one incoming edge, then no equivalence is created. */
+
+static void
+record_equivalences_from_incoming_edge (struct dom_walk_data *walk_data,
+ basic_block bb)
+{
+ int edge_flags;
+ basic_block parent;
+ struct eq_expr_value eq_expr_value;
+ tree parent_block_last_stmt = NULL;
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+
+ /* If our parent block ended with a control statment, then we may be
+ able to record some equivalences based on which outgoing edge from
+ the parent was followed. */
+ parent = get_immediate_dominator (CDI_DOMINATORS, bb);
+ if (parent)
+ {
+ parent_block_last_stmt = last_stmt (parent);
+ if (parent_block_last_stmt && !is_ctrl_stmt (parent_block_last_stmt))
+ parent_block_last_stmt = NULL;
+ }
+
+ eq_expr_value.src = NULL;
+ eq_expr_value.dst = NULL;
+
+ /* If we have a single predecessor, then extract EDGE_FLAGS from
+ our single incoming edge. Otherwise clear EDGE_FLAGS and
+ PARENT_BLOCK_LAST_STMT since they're not needed. */
+ if (bb->pred
+ && ! bb->pred->pred_next
+ && parent_block_last_stmt
+ && bb_for_stmt (parent_block_last_stmt) == bb->pred->src)
+ {
+ edge_flags = bb->pred->flags;
+ }
+ else
+ {
+ edge_flags = 0;
+ parent_block_last_stmt = NULL;
+ }
+
+ /* If our parent block ended in a COND_EXPR, add any equivalences
+ created by the COND_EXPR to the hash table and initialize
+ EQ_EXPR_VALUE appropriately.
+
+ EQ_EXPR_VALUE is an assignment expression created when BB's immediate
+ dominator ends in a COND_EXPR statement whose predicate is of the form
+ 'VAR == VALUE', where VALUE may be another variable or a constant.
+ This is used to propagate VALUE on the THEN_CLAUSE of that
+ conditional. This assignment is inserted in CONST_AND_COPIES so that
+ the copy and constant propagator can find more propagation
+ opportunities. */
+ if (parent_block_last_stmt
+ && bb->pred->pred_next == NULL
+ && TREE_CODE (parent_block_last_stmt) == COND_EXPR
+ && (edge_flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
+ eq_expr_value = get_eq_expr_value (parent_block_last_stmt,
+ (edge_flags & EDGE_TRUE_VALUE) != 0,
+ &bd->avail_exprs,
+ bb,
+ &bd->vrp_variables);
+ /* Similarly when the parent block ended in a SWITCH_EXPR. */
+ else if (parent_block_last_stmt
+ && bb->pred->pred_next == NULL
+ && TREE_CODE (parent_block_last_stmt) == SWITCH_EXPR)
+ {
+ tree switch_cond = SWITCH_COND (parent_block_last_stmt);
+
+ /* If the switch's condition is an SSA variable, then we may
+ know its value at each of the case labels. */
+ if (TREE_CODE (switch_cond) == SSA_NAME)
+ {
+ tree switch_vec = SWITCH_LABELS (parent_block_last_stmt);
+ size_t i, n = TREE_VEC_LENGTH (switch_vec);
+ int case_count = 0;
+ tree match_case = NULL_TREE;
+
+ /* Search the case labels for those whose destination is
+ the current basic block. */
+ for (i = 0; i < n; ++i)
+ {
+ tree elt = TREE_VEC_ELT (switch_vec, i);
+ if (label_to_block (CASE_LABEL (elt)) == bb)
+ {
+ if (++case_count > 1)
+ break;
+ match_case = elt;
+ }
+ }
+
+ /* If we encountered precisely one CASE_LABEL_EXPR and it
+ was not the default case, or a case range, then we know
+ the exact value of SWITCH_COND which caused us to get to
+ this block. Record that equivalence in EQ_EXPR_VALUE. */
+ if (case_count == 1
+ && CASE_LOW (match_case)
+ && !CASE_HIGH (match_case))
+ {
+ eq_expr_value.dst = switch_cond;
+ eq_expr_value.src = CASE_LOW (match_case);
+ }
+ }
+ }
+
+ /* If EQ_EXPR_VALUE (VAR == VALUE) is given, register the VALUE as a
+ new value for VAR, so that occurrences of VAR can be replaced with
+ VALUE while re-writing the THEN arm of a COND_EXPR. */
+ if (eq_expr_value.src && eq_expr_value.dst)
+ record_equality (eq_expr_value.dst, eq_expr_value.src,
+ &bd->const_and_copies);
+}
+
+/* Dump SSA statistics on FILE. */
+
+void
+dump_dominator_optimization_stats (FILE *file)
+{
+ long n_exprs;
+
+ fprintf (file, "Total number of statements: %6ld\n\n",
+ opt_stats.num_stmts);
+ fprintf (file, "Exprs considered for dominator optimizations: %6ld\n",
+ opt_stats.num_exprs_considered);
+
+ n_exprs = opt_stats.num_exprs_considered;
+ if (n_exprs == 0)
+ n_exprs = 1;
+
+ fprintf (file, " Redundant expressions eliminated: %6ld (%.0f%%)\n",
+ opt_stats.num_re, PERCENT (opt_stats.num_re,
+ n_exprs));
+
+ fprintf (file, "\nHash table statistics:\n");
+
+ fprintf (file, " avail_exprs: ");
+ htab_statistics (file, avail_exprs);
+}
+
+
+/* Dump SSA statistics on stderr. */
+
+void
+debug_dominator_optimization_stats (void)
+{
+ dump_dominator_optimization_stats (stderr);
+}
+
+
+/* Dump statistics for the hash table HTAB. */
+
+static void
+htab_statistics (FILE *file, htab_t htab)
+{
+ fprintf (file, "size %ld, %ld elements, %f collision/search ratio\n",
+ (long) htab_size (htab),
+ (long) htab_elements (htab),
+ htab_collisions (htab));
+}
+
+/* Record the fact that VAR has a nonzero value, though we may not know
+ its exact value. Note that if VAR is already known to have a nonzero
+ value, then we do nothing. */
+
+static void
+record_var_is_nonzero (tree var, varray_type *block_nonzero_vars_p)
+{
+ int indx = SSA_NAME_VERSION (var);
+
+ if (bitmap_bit_p (nonzero_vars, indx))
+ return;
+
+ /* Mark it in the global table. */
+ bitmap_set_bit (nonzero_vars, indx);
+
+ /* Record this SSA_NAME so that we can reset the global table
+ when we leave this block. */
+ if (! *block_nonzero_vars_p)
+ VARRAY_TREE_INIT (*block_nonzero_vars_p, 2, "block_nonzero_vars");
+ VARRAY_PUSH_TREE (*block_nonzero_vars_p, var);
+}
+
+/* Enter a statement into the true/false expression hash table indicating
+ that the condition COND has the value VALUE. */
+
+static void
+record_cond (tree cond, tree value, varray_type *block_avail_exprs_p)
+{
+ struct expr_hash_elt *element = xmalloc (sizeof (struct expr_hash_elt));
+ void **slot;
+
+ initialize_hash_element (cond, value, element);
+
+ slot = htab_find_slot_with_hash (avail_exprs, (void *)element,
+ element->hash, true);
+ if (*slot == NULL)
+ {
+ *slot = (void *) element;
+ if (! *block_avail_exprs_p)
+ VARRAY_TREE_INIT (*block_avail_exprs_p, 20, "block_avail_exprs");
+ VARRAY_PUSH_TREE (*block_avail_exprs_p, cond);
+ }
+ else
+ free (element);
+}
+
+/* A helper function for record_const_or_copy and record_equality.
+ Do the work of recording the value and undo info. */
+
+static void
+record_const_or_copy_1 (tree x, tree y, tree prev_x,
+ varray_type *block_const_and_copies_p)
+{
+ set_value_for (x, y, const_and_copies);
+
+ if (!*block_const_and_copies_p)
+ VARRAY_TREE_INIT (*block_const_and_copies_p, 2, "block_const_and_copies");
+ VARRAY_PUSH_TREE (*block_const_and_copies_p, x);
+ VARRAY_PUSH_TREE (*block_const_and_copies_p, prev_x);
+}
+
+/* Record that X is equal to Y in const_and_copies. Record undo
+ information in the block-local varray. */
+
+static void
+record_const_or_copy (tree x, tree y, varray_type *block_const_and_copies_p)
+{
+ tree prev_x = get_value_for (x, const_and_copies);
+
+ if (TREE_CODE (y) == SSA_NAME)
+ {
+ tree tmp = get_value_for (y, const_and_copies);
+ if (tmp)
+ y = tmp;
+ }
+
+ record_const_or_copy_1 (x, y, prev_x, block_const_and_copies_p);
+}
+
+/* Similarly, but assume that X and Y are the two operands of an EQ_EXPR.
+ This constrains the cases in which we may treat this as assignment. */
+
+static void
+record_equality (tree x, tree y, varray_type *block_const_and_copies_p)
+{
+ tree prev_x = NULL, prev_y = NULL;
+
+ if (TREE_CODE (x) == SSA_NAME)
+ prev_x = get_value_for (x, const_and_copies);
+ if (TREE_CODE (y) == SSA_NAME)
+ prev_y = get_value_for (y, const_and_copies);
+
+ /* If one of the previous values is invariant, then use that.
+ Otherwise it doesn't matter which value we choose, just so
+ long as we canonicalize on one value. */
+ if (TREE_INVARIANT (y))
+ ;
+ else if (TREE_INVARIANT (x))
+ prev_x = x, x = y, y = prev_x, prev_x = prev_y;
+ else if (prev_x && TREE_INVARIANT (prev_x))
+ x = y, y = prev_x, prev_x = prev_y;
+ else if (prev_y)
+ y = prev_y;
+
+ /* After the swapping, we must have one SSA_NAME. */
+ if (TREE_CODE (x) != SSA_NAME)
+ return;
+
+ /* For IEEE, -0.0 == 0.0, so we don't necessarily know the sign of a
+ variable compared against zero. If we're honoring signed zeros,
+ then we cannot record this value unless we know that the value is
+ non-zero. */
+ if (HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (x)))
+ && (TREE_CODE (y) != REAL_CST
+ || REAL_VALUES_EQUAL (dconst0, TREE_REAL_CST (y))))
+ return;
+
+ record_const_or_copy_1 (x, y, prev_x, block_const_and_copies_p);
+}
+
+/* STMT is a MODIFY_EXPR for which we were unable to find RHS in the
+ hash tables. Try to simplify the RHS using whatever equivalences
+ we may have recorded.
+
+ If we are able to simplify the RHS, then lookup the simplified form in
+ the hash table and return the result. Otherwise return NULL. */
+
+static tree
+simplify_rhs_and_lookup_avail_expr (struct dom_walk_data *walk_data,
+ tree stmt,
+ stmt_ann_t ann,
+ int insert)
+{
+ tree rhs = TREE_OPERAND (stmt, 1);
+ enum tree_code rhs_code = TREE_CODE (rhs);
+ tree result = NULL;
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+
+ /* If we have lhs = ~x, look and see if we earlier had x = ~y.
+ In which case we can change this statement to be lhs = y.
+ Which can then be copy propagated.
+
+ Similarly for negation. */
+ if ((rhs_code == BIT_NOT_EXPR || rhs_code == NEGATE_EXPR)
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+ {
+ /* Get the definition statement for our RHS. */
+ tree rhs_def_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 0));
+
+ /* See if the RHS_DEF_STMT has the same form as our statement. */
+ if (TREE_CODE (rhs_def_stmt) == MODIFY_EXPR
+ && TREE_CODE (TREE_OPERAND (rhs_def_stmt, 1)) == rhs_code)
+ {
+ tree rhs_def_operand;
+
+ rhs_def_operand = TREE_OPERAND (TREE_OPERAND (rhs_def_stmt, 1), 0);
+
+ /* Verify that RHS_DEF_OPERAND is a suitable SSA variable. */
+ if (TREE_CODE (rhs_def_operand) == SSA_NAME
+ && ! SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs_def_operand))
+ result = update_rhs_and_lookup_avail_expr (stmt,
+ rhs_def_operand,
+ &bd->avail_exprs,
+ ann,
+ insert);
+ }
+ }
+
+ /* If we have z = (x OP C1), see if we earlier had x = y OP C2.
+ If OP is associative, create and fold (y OP C2) OP C1 which
+ should result in (y OP C3), use that as the RHS for the
+ assignment. Add minus to this, as we handle it specially below. */
+ if ((associative_tree_code (rhs_code) || rhs_code == MINUS_EXPR)
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
+ && is_gimple_min_invariant (TREE_OPERAND (rhs, 1)))
+ {
+ tree rhs_def_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 0));
+
+ /* See if the RHS_DEF_STMT has the same form as our statement. */
+ if (TREE_CODE (rhs_def_stmt) == MODIFY_EXPR)
+ {
+ tree rhs_def_rhs = TREE_OPERAND (rhs_def_stmt, 1);
+ enum tree_code rhs_def_code = TREE_CODE (rhs_def_rhs);
+
+ if (rhs_code == rhs_def_code
+ || (rhs_code == PLUS_EXPR && rhs_def_code == MINUS_EXPR)
+ || (rhs_code == MINUS_EXPR && rhs_def_code == PLUS_EXPR))
+ {
+ tree def_stmt_op0 = TREE_OPERAND (rhs_def_rhs, 0);
+ tree def_stmt_op1 = TREE_OPERAND (rhs_def_rhs, 1);
+
+ if (TREE_CODE (def_stmt_op0) == SSA_NAME
+ && ! SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def_stmt_op0)
+ && is_gimple_min_invariant (def_stmt_op1))
+ {
+ tree outer_const = TREE_OPERAND (rhs, 1);
+ tree type = TREE_TYPE (TREE_OPERAND (stmt, 0));
+ tree t;
+
+ /* Ho hum. So fold will only operate on the outermost
+ thingy that we give it, so we have to build the new
+ expression in two pieces. This requires that we handle
+ combinations of plus and minus. */
+ if (rhs_def_code != rhs_code)
+ {
+ if (rhs_def_code == MINUS_EXPR)
+ t = build (MINUS_EXPR, type, outer_const, def_stmt_op1);
+ else
+ t = build (MINUS_EXPR, type, def_stmt_op1, outer_const);
+ rhs_code = PLUS_EXPR;
+ }
+ else if (rhs_def_code == MINUS_EXPR)
+ t = build (PLUS_EXPR, type, def_stmt_op1, outer_const);
+ else
+ t = build (rhs_def_code, type, def_stmt_op1, outer_const);
+ t = local_fold (t);
+ t = build (rhs_code, type, def_stmt_op0, t);
+ t = local_fold (t);
+
+ /* If the result is a suitable looking gimple expression,
+ then use it instead of the original for STMT. */
+ if (TREE_CODE (t) == SSA_NAME
+ || (TREE_CODE_CLASS (TREE_CODE (t)) == '1'
+ && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME)
+ || ((TREE_CODE_CLASS (TREE_CODE (t)) == '2'
+ || TREE_CODE_CLASS (TREE_CODE (t)) == '<')
+ && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
+ && is_gimple_val (TREE_OPERAND (t, 1))))
+ result = update_rhs_and_lookup_avail_expr
+ (stmt, t, &bd->avail_exprs, ann, insert);
+ }
+ }
+ }
+ }
+
+ /* Transform TRUNC_DIV_EXPR and TRUNC_MOD_EXPR into RSHIFT_EXPR
+ and BIT_AND_EXPR respectively if the first operand is greater
+ than zero and the second operand is an exact power of two. */
+ if ((rhs_code == TRUNC_DIV_EXPR || rhs_code == TRUNC_MOD_EXPR)
+ && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (rhs, 0)))
+ && integer_pow2p (TREE_OPERAND (rhs, 1)))
+ {
+ tree val;
+ tree op = TREE_OPERAND (rhs, 0);
+
+ if (TYPE_UNSIGNED (TREE_TYPE (op)))
+ {
+ val = integer_one_node;
+ }
+ else
+ {
+ tree dummy_cond = walk_data->global_data;
+
+ if (! dummy_cond)
+ {
+ dummy_cond = build (GT_EXPR, boolean_type_node,
+ op, integer_zero_node);
+ dummy_cond = build (COND_EXPR, void_type_node,
+ dummy_cond, NULL, NULL);
+ walk_data->global_data = dummy_cond;
+ }
+ else
+ {
+ TREE_SET_CODE (TREE_OPERAND (dummy_cond, 0), GT_EXPR);
+ TREE_OPERAND (TREE_OPERAND (dummy_cond, 0), 0) = op;
+ TREE_OPERAND (TREE_OPERAND (dummy_cond, 0), 1)
+ = integer_zero_node;
+ }
+ val = simplify_cond_and_lookup_avail_expr (dummy_cond,
+ &bd->avail_exprs,
+ NULL, false);
+ }
+
+ if (val && integer_onep (val))
+ {
+ tree t;
+ tree op0 = TREE_OPERAND (rhs, 0);
+ tree op1 = TREE_OPERAND (rhs, 1);
+
+ if (rhs_code == TRUNC_DIV_EXPR)
+ t = build (RSHIFT_EXPR, TREE_TYPE (op0), op0,
+ build_int_2 (tree_log2 (op1), 0));
+ else
+ t = build (BIT_AND_EXPR, TREE_TYPE (op0), op0,
+ local_fold (build (MINUS_EXPR, TREE_TYPE (op1),
+ op1, integer_one_node)));
+
+ result = update_rhs_and_lookup_avail_expr (stmt, t,
+ &bd->avail_exprs,
+ ann, insert);
+ }
+ }
+
+ /* Transform ABS (X) into X or -X as appropriate. */
+ if (rhs_code == ABS_EXPR
+ && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (rhs, 0))))
+ {
+ tree val;
+ tree op = TREE_OPERAND (rhs, 0);
+ tree type = TREE_TYPE (op);
+
+ if (TYPE_UNSIGNED (type))
+ {
+ val = integer_zero_node;
+ }
+ else
+ {
+ tree dummy_cond = walk_data->global_data;
+
+ if (! dummy_cond)
+ {
+ dummy_cond = build (LT_EXPR, boolean_type_node,
+ op, integer_zero_node);
+ dummy_cond = build (COND_EXPR, void_type_node,
+ dummy_cond, NULL, NULL);
+ walk_data->global_data = dummy_cond;
+ }
+ else
+ {
+ TREE_SET_CODE (TREE_OPERAND (dummy_cond, 0), LT_EXPR);
+ TREE_OPERAND (TREE_OPERAND (dummy_cond, 0), 0) = op;
+ TREE_OPERAND (TREE_OPERAND (dummy_cond, 0), 1)
+ = convert (type, integer_zero_node);
+ }
+ val = simplify_cond_and_lookup_avail_expr (dummy_cond,
+ &bd->avail_exprs,
+ NULL, false);
+ }
+
+ if (val
+ && (integer_onep (val) || integer_zerop (val)))
+ {
+ tree t;
+
+ if (integer_onep (val))
+ t = build1 (NEGATE_EXPR, TREE_TYPE (op), op);
+ else
+ t = op;
+
+ result = update_rhs_and_lookup_avail_expr (stmt, t,
+ &bd->avail_exprs,
+ ann, insert);
+ }
+ }
+
+ /* Optimize *"foo" into 'f'. This is done here rather than
+ in fold to avoid problems with stuff like &*"foo". */
+ if (TREE_CODE (rhs) == INDIRECT_REF || TREE_CODE (rhs) == ARRAY_REF)
+ {
+ tree t = fold_read_from_constant_string (rhs);
+
+ if (t)
+ result = update_rhs_and_lookup_avail_expr (stmt, t,
+ &bd->avail_exprs,
+ ann, insert);
+ }
+
+ return result;
+}
+
+/* COND is a condition of the form:
+
+ x == const or x != const
+
+ Look back to x's defining statement and see if x is defined as
+
+ x = (type) y;
+
+ If const is unchanged if we convert it to type, then we can build
+ the equivalent expression:
+
+
+ y == const or y != const
+
+ Which may allow further optimizations.
+
+ Return the equivalent comparison or NULL if no such equivalent comparison
+ was found. */
+
+static tree
+find_equivalent_equality_comparison (tree cond)
+{
+ tree op0 = TREE_OPERAND (cond, 0);
+ tree op1 = TREE_OPERAND (cond, 1);
+ tree def_stmt = SSA_NAME_DEF_STMT (op0);
+
+ /* OP0 might have been a parameter, so first make sure it
+ was defined by a MODIFY_EXPR. */
+ if (def_stmt && TREE_CODE (def_stmt) == MODIFY_EXPR)
+ {
+ tree def_rhs = TREE_OPERAND (def_stmt, 1);
+
+ /* Now make sure the RHS of the MODIFY_EXPR is a typecast. */
+ if ((TREE_CODE (def_rhs) == NOP_EXPR
+ || TREE_CODE (def_rhs) == CONVERT_EXPR)
+ && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME)
+ {
+ tree def_rhs_inner = TREE_OPERAND (def_rhs, 0);
+ tree def_rhs_inner_type = TREE_TYPE (def_rhs_inner);
+ tree new;
+
+ if (TYPE_PRECISION (def_rhs_inner_type)
+ > TYPE_PRECISION (TREE_TYPE (def_rhs)))
+ return NULL;
+
+ /* What we want to prove is that if we convert OP1 to
+ the type of the object inside the NOP_EXPR that the
+ result is still equivalent to SRC.
+
+ If that is true, the build and return new equivalent
+ condition which uses the source of the typecast and the
+ new constant (which has only changed its type). */
+ new = build1 (TREE_CODE (def_rhs), def_rhs_inner_type, op1);
+ new = local_fold (new);
+ if (is_gimple_val (new) && tree_int_cst_equal (new, op1))
+ return build (TREE_CODE (cond), TREE_TYPE (cond),
+ def_rhs_inner, new);
+ }
+ }
+ return NULL;
+}
+
+/* STMT is a COND_EXPR for which we could not trivially determine its
+ result. This routine attempts to find equivalent forms of the
+ condition which we may be able to optimize better. It also
+ uses simple value range propagation to optimize conditionals. */
+
+static tree
+simplify_cond_and_lookup_avail_expr (tree stmt,
+ varray_type *block_avail_exprs_p,
+ stmt_ann_t ann,
+ int insert)
+{
+ tree cond = COND_EXPR_COND (stmt);
+
+ if (TREE_CODE_CLASS (TREE_CODE (cond)) == '<')
+ {
+ tree op0 = TREE_OPERAND (cond, 0);
+ tree op1 = TREE_OPERAND (cond, 1);
+
+ if (TREE_CODE (op0) == SSA_NAME && is_gimple_min_invariant (op1))
+ {
+ int limit;
+ tree low, high, cond_low, cond_high;
+ int lowequal, highequal, swapped, no_overlap, subset, cond_inverted;
+ varray_type vrp_records;
+ struct vrp_element *element;
+
+ /* First see if we have test of an SSA_NAME against a constant
+ where the SSA_NAME is defined by an earlier typecast which
+ is irrelevant when performing tests against the given
+ constant. */
+ if (TREE_CODE (cond) == EQ_EXPR || TREE_CODE (cond) == NE_EXPR)
+ {
+ tree new_cond = find_equivalent_equality_comparison (cond);
+
+ if (new_cond)
+ {
+ /* Update the statement to use the new equivalent
+ condition. */
+ COND_EXPR_COND (stmt) = new_cond;
+ ann->modified = 1;
+
+ /* Lookup the condition and return its known value if it
+ exists. */
+ new_cond = lookup_avail_expr (stmt, block_avail_exprs_p,
+ insert);
+ if (new_cond)
+ return new_cond;
+
+ /* The operands have changed, so update op0 and op1. */
+ op0 = TREE_OPERAND (cond, 0);
+ op1 = TREE_OPERAND (cond, 1);
+ }
+ }
+
+ /* Consult the value range records for this variable (if they exist)
+ to see if we can eliminate or simplify this conditional.
+
+ Note two tests are necessary to determine no records exist.
+ First we have to see if the virtual array exists, if it
+ exists, then we have to check its active size.
+
+ Also note the vast majority of conditionals are not testing
+ a variable which has had its range constrained by an earlier
+ conditional. So this filter avoids a lot of unnecessary work. */
+ vrp_records = VARRAY_GENERIC_PTR (vrp_data, SSA_NAME_VERSION (op0));
+ if (vrp_records == NULL)
+ return NULL;
+
+ limit = VARRAY_ACTIVE_SIZE (vrp_records);
+
+ /* If we have no value range records for this variable, or we are
+ unable to extract a range for this condition, then there is
+ nothing to do. */
+ if (limit == 0
+ || ! extract_range_from_cond (cond, &cond_high,
+ &cond_low, &cond_inverted))
+ return NULL;
+
+ /* We really want to avoid unnecessary computations of range
+ info. So all ranges are computed lazily; this avoids a
+ lot of unnecessary work. ie, we record the conditional,
+ but do not process how it constrains the variable's
+ potential values until we know that processing the condition
+ could be helpful.
+
+ However, we do not want to have to walk a potentially long
+ list of ranges, nor do we want to compute a variable's
+ range more than once for a given path.
+
+ Luckily, each time we encounter a conditional that can not
+ be otherwise optimized we will end up here and we will
+ compute the necessary range information for the variable
+ used in this condition.
+
+ Thus you can conclude that there will never be more than one
+ conditional associated with a variable which has not been
+ processed. So we never need to merge more than one new
+ conditional into the current range.
+
+ These properties also help us avoid unnecessary work. */
+ element
+ = (struct vrp_element *)VARRAY_GENERIC_PTR (vrp_records, limit - 1);
+
+ if (element->high && element->low)
+ {
+ /* The last element has been processed, so there is no range
+ merging to do, we can simply use the high/low values
+ recorded in the last element. */
+ low = element->low;
+ high = element->high;
+ }
+ else
+ {
+ tree tmp_high, tmp_low;
+ int dummy;
+
+ /* The last element has not been processed. Process it now. */
+ extract_range_from_cond (element->cond, &tmp_high,
+ &tmp_low, &dummy);
+
+ /* If this is the only element, then no merging is necessary,
+ the high/low values from extract_range_from_cond are all
+ we need. */
+ if (limit == 1)
+ {
+ low = tmp_low;
+ high = tmp_high;
+ }
+ else
+ {
+ /* Get the high/low value from the previous element. */
+ struct vrp_element *prev
+ = (struct vrp_element *)VARRAY_GENERIC_PTR (vrp_records,
+ limit - 2);
+ low = prev->low;
+ high = prev->high;
+
+ /* Merge in this element's range with the range from the
+ previous element.
+
+ The low value for the merged range is the maximum of
+ the previous low value and the low value of this record.
+
+ Similarly the high value for the merged range is the
+ minimum of the previous high value and the high value of
+ this record. */
+ low = (tree_int_cst_compare (low, tmp_low) == 1
+ ? low : tmp_low);
+ high = (tree_int_cst_compare (high, tmp_high) == -1
+ ? high : tmp_high);
+ }
+
+ /* And record the computed range. */
+ element->low = low;
+ element->high = high;
+
+ }
+
+ /* After we have constrained this variable's potential values,
+ we try to determine the result of the given conditional.
+
+ To simplify later tests, first determine if the current
+ low value is the same low value as the conditional.
+ Similarly for the current high value and the high value
+ for the conditional. */
+ lowequal = tree_int_cst_equal (low, cond_low);
+ highequal = tree_int_cst_equal (high, cond_high);
+
+ if (lowequal && highequal)
+ return (cond_inverted ? boolean_false_node : boolean_true_node);
+
+ /* To simplify the overlap/subset tests below we may want
+ to swap the two ranges so that the larger of the two
+ ranges occurs "first". */
+ swapped = 0;
+ if (tree_int_cst_compare (low, cond_low) == 1
+ || (lowequal
+ && tree_int_cst_compare (cond_high, high) == 1))
+ {
+ tree temp;
+
+ swapped = 1;
+ temp = low;
+ low = cond_low;
+ cond_low = temp;
+ temp = high;
+ high = cond_high;
+ cond_high = temp;
+ }
+
+ /* Now determine if there is no overlap in the ranges
+ or if the second range is a subset of the first range. */
+ no_overlap = tree_int_cst_lt (high, cond_low);
+ subset = tree_int_cst_compare (cond_high, high) != 1;
+
+ /* If there was no overlap in the ranges, then this conditional
+ always has a false value (unless we had to invert this
+ conditional, in which case it always has a true value). */
+ if (no_overlap)
+ return (cond_inverted ? boolean_true_node : boolean_false_node);
+
+ /* If the current range is a subset of the condition's range,
+ then this conditional always has a true value (unless we
+ had to invert this conditional, in which case it always
+ has a true value). */
+ if (subset && swapped)
+ return (cond_inverted ? boolean_false_node : boolean_true_node);
+
+ /* We were unable to determine the result of the conditional.
+ However, we may be able to simplify the conditional. First
+ merge the ranges in the same manner as range merging above. */
+ low = tree_int_cst_compare (low, cond_low) == 1 ? low : cond_low;
+ high = tree_int_cst_compare (high, cond_high) == -1 ? high : cond_high;
+
+ /* If the range has converged to a single point, then turn this
+ into an equality comparison. */
+ if (TREE_CODE (cond) != EQ_EXPR
+ && TREE_CODE (cond) != NE_EXPR
+ && tree_int_cst_equal (low, high))
+ {
+ TREE_SET_CODE (cond, EQ_EXPR);
+ TREE_OPERAND (cond, 1) = high;
+ }
+ }
+ }
+ return 0;
+}
+
+/* STMT is a SWITCH_EXPR for which we could not trivially determine its
+ result. This routine attempts to find equivalent forms of the
+ condition which we may be able to optimize better. */
+
+static tree
+simplify_switch_and_lookup_avail_expr (tree stmt,
+ varray_type *block_avail_exprs_p,
+ stmt_ann_t ann,
+ int insert)
+{
+ tree cond = SWITCH_COND (stmt);
+ tree def, to, ti;
+
+ /* The optimization that we really care about is removing unnecessary
+ casts. That will let us do much better in propagating the inferred
+ constant at the switch target. */
+ if (TREE_CODE (cond) == SSA_NAME)
+ {
+ def = SSA_NAME_DEF_STMT (cond);
+ if (TREE_CODE (def) == MODIFY_EXPR)
+ {
+ def = TREE_OPERAND (def, 1);
+ if (TREE_CODE (def) == NOP_EXPR)
+ {
+ def = TREE_OPERAND (def, 0);
+ to = TREE_TYPE (cond);
+ ti = TREE_TYPE (def);
+
+ /* If we have an extension that preserves sign, then we
+ can copy the source value into the switch. */
+ if (TYPE_UNSIGNED (to) == TYPE_UNSIGNED (ti)
+ && TYPE_PRECISION (to) >= TYPE_PRECISION (ti)
+ && is_gimple_val (def))
+ {
+ SWITCH_COND (stmt) = def;
+ ann->modified = 1;
+
+ return lookup_avail_expr (stmt, block_avail_exprs_p, insert);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Propagate known constants/copies into PHI nodes of BB's successor
+ blocks. */
+
+static void
+cprop_into_phis (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
+ basic_block bb)
+{
+ cprop_into_successor_phis (bb, const_and_copies);
+}
+
+/* Search for redundant computations in STMT. If any are found, then
+ replace them with the variable holding the result of the computation.
+
+ If safe, record this expression into the available expression hash
+ table. */
+
+static bool
+eliminate_redundant_computations (struct dom_walk_data *walk_data,
+ tree stmt, stmt_ann_t ann)
+{
+ vdef_optype vdefs = VDEF_OPS (ann);
+ tree *expr_p, def = NULL_TREE;
+ bool insert = true;
+ tree cached_lhs;
+ bool retval = false;
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+
+ if (TREE_CODE (stmt) == MODIFY_EXPR)
+ def = TREE_OPERAND (stmt, 0);
+
+ /* Certain expressions on the RHS can be optimized away, but can not
+ themselves be entered into the hash tables. */
+ if (ann->makes_aliased_stores
+ || ! def
+ || TREE_CODE (def) != SSA_NAME
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def)
+ || NUM_VDEFS (vdefs) != 0)
+ insert = false;
+
+ /* Check if the expression has been computed before. */
+ cached_lhs = lookup_avail_expr (stmt, &bd->avail_exprs, insert);
+
+ /* If this is an assignment and the RHS was not in the hash table,
+ then try to simplify the RHS and lookup the new RHS in the
+ hash table. */
+ if (! cached_lhs && TREE_CODE (stmt) == MODIFY_EXPR)
+ cached_lhs = simplify_rhs_and_lookup_avail_expr (walk_data,
+ stmt,
+ ann,
+ insert);
+ /* Similarly if this is a COND_EXPR and we did not find its
+ expression in the hash table, simplify the condition and
+ try again. */
+ else if (! cached_lhs && TREE_CODE (stmt) == COND_EXPR)
+ cached_lhs = simplify_cond_and_lookup_avail_expr (stmt,
+ &bd->avail_exprs,
+ ann,
+ insert);
+ /* Similarly for a SWITCH_EXPR. */
+ else if (!cached_lhs && TREE_CODE (stmt) == SWITCH_EXPR)
+ cached_lhs = simplify_switch_and_lookup_avail_expr (stmt,
+ &bd->avail_exprs,
+ ann,
+ insert);
+
+ opt_stats.num_exprs_considered++;
+
+ /* Get a pointer to the expression we are trying to optimize. */
+ if (TREE_CODE (stmt) == COND_EXPR)
+ expr_p = &COND_EXPR_COND (stmt);
+ else if (TREE_CODE (stmt) == SWITCH_EXPR)
+ expr_p = &SWITCH_COND (stmt);
+ else if (TREE_CODE (stmt) == RETURN_EXPR && TREE_OPERAND (stmt, 0))
+ expr_p = &TREE_OPERAND (TREE_OPERAND (stmt, 0), 1);
+ else
+ expr_p = &TREE_OPERAND (stmt, 1);
+
+ /* It is safe to ignore types here since we have already done
+ type checking in the hashing and equality routines. In fact
+ type checking here merely gets in the way of constant
+ propagation. Also, make sure that it is safe to propagate
+ CACHED_LHS into *EXPR_P. */
+ if (cached_lhs
+ && (TREE_CODE (cached_lhs) != SSA_NAME
+ || may_propagate_copy (cached_lhs, *expr_p)))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, " Replaced redundant expr '");
+ print_generic_expr (dump_file, *expr_p, dump_flags);
+ fprintf (dump_file, "' with '");
+ print_generic_expr (dump_file, cached_lhs, dump_flags);
+ fprintf (dump_file, "'\n");
+ }
+
+ opt_stats.num_re++;
+
+#if defined ENABLE_CHECKING
+ if (TREE_CODE (cached_lhs) != SSA_NAME
+ && !is_gimple_min_invariant (cached_lhs))
+ abort ();
+#endif
+
+ if (TREE_CODE (cached_lhs) == ADDR_EXPR
+ || (POINTER_TYPE_P (TREE_TYPE (*expr_p))
+ && is_gimple_min_invariant (cached_lhs)))
+ retval = true;
+
+ propagate_value (expr_p, cached_lhs);
+ ann->modified = 1;
+ }
+ return retval;
+}
+
+/* STMT, a MODIFY_EXPR, may create certain equivalences, in either
+ the available expressions table or the const_and_copies table.
+ Detect and record those equivalences. */
+
+static void
+record_equivalences_from_stmt (tree stmt,
+ varray_type *block_avail_exprs_p,
+ varray_type *block_nonzero_vars_p,
+ int may_optimize_p,
+ stmt_ann_t ann)
+{
+ tree lhs = TREE_OPERAND (stmt, 0);
+ enum tree_code lhs_code = TREE_CODE (lhs);
+ int i;
+
+ if (lhs_code == SSA_NAME)
+ {
+ tree rhs = TREE_OPERAND (stmt, 1);
+
+ /* Strip away any useless type conversions. */
+ STRIP_USELESS_TYPE_CONVERSION (rhs);
+
+ /* If the RHS of the assignment is a constant or another variable that
+ may be propagated, register it in the CONST_AND_COPIES table. We
+ do not need to record unwind data for this, since this is a true
+ assignment and not an equivalence infered from a comparison. All
+ uses of this ssa name are dominated by this assignment, so unwinding
+ just costs time and space. */
+ if (may_optimize_p
+ && (TREE_CODE (rhs) == SSA_NAME
+ || is_gimple_min_invariant (rhs)))
+ set_value_for (lhs, rhs, const_and_copies);
+
+ /* alloca never returns zero and the address of a non-weak symbol
+ is never zero. NOP_EXPRs and CONVERT_EXPRs can be completely
+ stripped as they do not affect this equivalence. */
+ while (TREE_CODE (rhs) == NOP_EXPR
+ || TREE_CODE (rhs) == CONVERT_EXPR)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ if (alloca_call_p (rhs)
+ || (TREE_CODE (rhs) == ADDR_EXPR
+ && DECL_P (TREE_OPERAND (rhs, 0))
+ && ! DECL_WEAK (TREE_OPERAND (rhs, 0))))
+ record_var_is_nonzero (lhs, block_nonzero_vars_p);
+
+ /* IOR of any value with a nonzero value will result in a nonzero
+ value. Even if we do not know the exact result recording that
+ the result is nonzero is worth the effort. */
+ if (TREE_CODE (rhs) == BIT_IOR_EXPR
+ && integer_nonzerop (TREE_OPERAND (rhs, 1)))
+ record_var_is_nonzero (lhs, block_nonzero_vars_p);
+ }
+
+ /* Look at both sides for pointer dereferences. If we find one, then
+ the pointer must be nonnull and we can enter that equivalence into
+ the hash tables. */
+ for (i = 0; i < 2; i++)
+ {
+ tree t = TREE_OPERAND (stmt, i);
+
+ /* Strip away any COMPONENT_REFs. */
+ while (TREE_CODE (t) == COMPONENT_REF)
+ t = TREE_OPERAND (t, 0);
+
+ /* Now see if this is a pointer dereference. */
+ if (TREE_CODE (t) == INDIRECT_REF)
+ {
+ tree op = TREE_OPERAND (t, 0);
+
+ /* If the pointer is a SSA variable, then enter new
+ equivalences into the hash table. */
+ if (TREE_CODE (op) == SSA_NAME)
+ record_var_is_nonzero (op, block_nonzero_vars_p);
+ }
+ }
+
+ /* A memory store, even an aliased store, creates a useful
+ equivalence. By exchanging the LHS and RHS, creating suitable
+ vops and recording the result in the available expression table,
+ we may be able to expose more redundant loads. */
+ if (!ann->has_volatile_ops
+ && (TREE_CODE (TREE_OPERAND (stmt, 1)) == SSA_NAME
+ || is_gimple_min_invariant (TREE_OPERAND (stmt, 1)))
+ && !is_gimple_reg (lhs))
+ {
+ tree rhs = TREE_OPERAND (stmt, 1);
+ tree new;
+ size_t j;
+
+ /* FIXME: If the LHS of the assignment is a bitfield and the RHS
+ is a constant, we need to adjust the constant to fit into the
+ type of the LHS. If the LHS is a bitfield and the RHS is not
+ a constant, then we can not record any equivalences for this
+ statement since we would need to represent the widening or
+ narrowing of RHS. This fixes gcc.c-torture/execute/921016-1.c
+ and should not be necessary if GCC represented bitfields
+ properly. */
+ if (lhs_code == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (lhs, 1)))
+ {
+ if (TREE_CONSTANT (rhs))
+ rhs = widen_bitfield (rhs, TREE_OPERAND (lhs, 1), lhs);
+ else
+ rhs = NULL;
+
+ /* If the value overflowed, then we can not use this equivalence. */
+ if (rhs && ! is_gimple_min_invariant (rhs))
+ rhs = NULL;
+ }
+
+ if (rhs)
+ {
+ vdef_optype vdefs = VDEF_OPS (ann);
+
+ /* Build a new statement with the RHS and LHS exchanged. */
+ new = build (MODIFY_EXPR, TREE_TYPE (stmt), rhs, lhs);
+
+ /* Get an annotation and set up the real operands. */
+ get_stmt_ann (new);
+ get_stmt_operands (new);
+
+ /* Clear out the virtual operands on the new statement, we are
+ going to set them explicitly below. */
+ remove_vuses (new);
+ remove_vdefs (new);
+
+ start_ssa_stmt_operands (new);
+ /* For each VDEF on the original statement, we want to create a
+ VUSE of the VDEF result on the new statement. */
+ for (j = 0; j < NUM_VDEFS (vdefs); j++)
+ {
+ tree op = VDEF_RESULT (vdefs, j);
+ add_vuse (op, new);
+ }
+
+ finalize_ssa_stmt_operands (new);
+
+ /* Finally enter the statement into the available expression
+ table. */
+ lookup_avail_expr (new, block_avail_exprs_p, true);
+ }
+ }
+}
+
+/* Optimize the statement pointed by iterator SI.
+
+ We try to perform some simplistic global redundancy elimination and
+ constant propagation:
+
+ 1- To detect global redundancy, we keep track of expressions that have
+ been computed in this block and its dominators. If we find that the
+ same expression is computed more than once, we eliminate repeated
+ computations by using the target of the first one.
+
+ 2- Constant values and copy assignments. This is used to do very
+ simplistic constant and copy propagation. When a constant or copy
+ assignment is found, we map the value on the RHS of the assignment to
+ the variable in the LHS in the CONST_AND_COPIES table. */
+
+static void
+optimize_stmt (struct dom_walk_data *walk_data,
+ basic_block bb ATTRIBUTE_UNUSED,
+ block_stmt_iterator si)
+{
+ stmt_ann_t ann;
+ tree stmt;
+ vdef_optype vdefs;
+ bool may_optimize_p;
+ bool may_have_exposed_new_symbols = false;
+ struct dom_walk_block_data *bd
+ = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack);
+
+ stmt = bsi_stmt (si);
+
+ get_stmt_operands (stmt);
+ ann = stmt_ann (stmt);
+ vdefs = VDEF_OPS (ann);
+ opt_stats.num_stmts++;
+ may_have_exposed_new_symbols = false;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Optimizing statement ");
+ print_generic_stmt (dump_file, stmt, TDF_SLIM);
+ }
+
+ /* Const/copy propagate into USES, VUSES and the RHS of VDEFs. */
+ may_have_exposed_new_symbols = cprop_into_stmt (stmt, const_and_copies);
+
+ /* If the statement has been modified with constant replacements,
+ fold its RHS before checking for redundant computations. */
+ if (ann->modified)
+ {
+ /* Try to fold the statement making sure that STMT is kept
+ up to date. */
+ if (fold_stmt (bsi_stmt_ptr (si)))
+ {
+ stmt = bsi_stmt (si);
+ ann = stmt_ann (stmt);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, " Folded to: ");
+ print_generic_stmt (dump_file, stmt, TDF_SLIM);
+ }
+ }
+
+ /* Constant/copy propagation above may change the set of
+ virtual operands associated with this statement. Folding
+ may remove the need for some virtual operands.
+
+ Indicate we will need to rescan and rewrite the statement. */
+ may_have_exposed_new_symbols = true;
+ }
+
+ /* Check for redundant computations. Do this optimization only
+ for assignments that have no volatile ops and conditionals. */
+ may_optimize_p = (!ann->has_volatile_ops
+ && ((TREE_CODE (stmt) == RETURN_EXPR
+ && TREE_OPERAND (stmt, 0)
+ && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR
+ && ! (TREE_SIDE_EFFECTS
+ (TREE_OPERAND (TREE_OPERAND (stmt, 0), 1))))
+ || (TREE_CODE (stmt) == MODIFY_EXPR
+ && ! TREE_SIDE_EFFECTS (TREE_OPERAND (stmt, 1)))
+ || TREE_CODE (stmt) == COND_EXPR
+ || TREE_CODE (stmt) == SWITCH_EXPR));
+
+ if (may_optimize_p)
+ may_have_exposed_new_symbols
+ |= eliminate_redundant_computations (walk_data, stmt, ann);
+
+ /* Record any additional equivalences created by this statement. */
+ if (TREE_CODE (stmt) == MODIFY_EXPR)
+ record_equivalences_from_stmt (stmt,
+ &bd->avail_exprs,
+ &bd->nonzero_vars,
+ may_optimize_p,
+ ann);
+
+ register_definitions_for_stmt (ann, &bd->block_defs);
+
+ /* If STMT is a COND_EXPR and it was modified, then we may know
+ where it goes. If that is the case, then mark the CFG as altered.
+
+ This will cause us to later call remove_unreachable_blocks and
+ cleanup_tree_cfg when it is safe to do so. It is not safe to
+ clean things up here since removal of edges and such can trigger
+ the removal of PHI nodes, which in turn can release SSA_NAMEs to
+ the manager.
+
+ That's all fine and good, except that once SSA_NAMEs are released
+ to the manager, we must not call create_ssa_name until all references
+ to released SSA_NAMEs have been eliminated.
+
+ All references to the deleted SSA_NAMEs can not be eliminated until
+ we remove unreachable blocks.
+
+ We can not remove unreachable blocks until after we have completed
+ any queued jump threading.
+
+ We can not complete any queued jump threads until we have taken
+ appropriate variables out of SSA form. Taking variables out of
+ SSA form can call create_ssa_name and thus we lose.
+
+ Ultimately I suspect we're going to need to change the interface
+ into the SSA_NAME manager. */
+
+ if (ann->modified)
+ {
+ tree val = NULL;
+
+ if (TREE_CODE (stmt) == COND_EXPR)
+ val = COND_EXPR_COND (stmt);
+ else if (TREE_CODE (stmt) == SWITCH_EXPR)
+ val = SWITCH_COND (stmt);
+
+ if (val && TREE_CODE (val) == INTEGER_CST
+ && find_taken_edge (bb_for_stmt (stmt), val))
+ cfg_altered = true;
+ }
+
+ if (may_have_exposed_new_symbols)
+ {
+ if (! bd->stmts_to_rescan)
+ VARRAY_TREE_INIT (bd->stmts_to_rescan, 20, "stmts_to_rescan");
+ VARRAY_PUSH_TREE (bd->stmts_to_rescan, bsi_stmt (si));
+ }
+}
+
+/* Replace the RHS of STMT with NEW_RHS. If RHS can be found in the
+ available expression hashtable, then return the LHS from the hash
+ table.
+
+ If INSERT is true, then we also update the available expression
+ hash table to account for the changes made to STMT. */
+
+static tree
+update_rhs_and_lookup_avail_expr (tree stmt, tree new_rhs,
+ varray_type *block_avail_exprs_p,
+ stmt_ann_t ann,
+ bool insert)
+{
+ tree cached_lhs = NULL;
+
+ /* Remove the old entry from the hash table. */
+ if (insert)
+ {
+ struct expr_hash_elt element;
+
+ initialize_hash_element (stmt, NULL, &element);
+ htab_remove_elt_with_hash (avail_exprs, &element, element.hash);
+ }
+
+ /* Now update the RHS of the assignment. */
+ TREE_OPERAND (stmt, 1) = new_rhs;
+
+ /* Now lookup the updated statement in the hash table. */
+ cached_lhs = lookup_avail_expr (stmt, block_avail_exprs_p, insert);
+
+ /* We have now called lookup_avail_expr twice with two different
+ versions of this same statement, once in optimize_stmt, once here.
+
+ We know the call in optimize_stmt did not find an existing entry
+ in the hash table, so a new entry was created. At the same time
+ this statement was pushed onto the BLOCK_AVAIL_EXPRS varray.
+
+ If this call failed to find an existing entry on the hash table,
+ then the new version of this statement was entered into the
+ hash table. And this statement was pushed onto BLOCK_AVAIL_EXPR
+ for the second time. So there are two copies on BLOCK_AVAIL_EXPRs
+
+ If this call succeeded, we still have one copy of this statement
+ on the BLOCK_AVAIL_EXPRs varray.
+
+ For both cases, we need to pop the most recent entry off the
+ BLOCK_AVAIL_EXPRs varray. For the case where we never found this
+ statement in the hash tables, that will leave precisely one
+ copy of this statement on BLOCK_AVAIL_EXPRs. For the case where
+ we found a copy of this statement in the second hash table lookup
+ we want _no_ copies of this statement in BLOCK_AVAIL_EXPRs. */
+ if (insert)
+ VARRAY_POP (*block_avail_exprs_p);
+
+ /* And make sure we record the fact that we modified this
+ statement. */
+ ann->modified = 1;
+
+ return cached_lhs;
+}
+
+/* Search for an existing instance of STMT in the AVAIL_EXPRS table. If
+ found, return its LHS. Otherwise insert STMT in the table and return
+ NULL_TREE.
+
+ Also, when an expression is first inserted in the AVAIL_EXPRS table, it
+ is also added to the stack pointed by BLOCK_AVAIL_EXPRS_P, so that they
+ can be removed when we finish processing this block and its children.
+
+ NOTE: This function assumes that STMT is a MODIFY_EXPR node that
+ contains no CALL_EXPR on its RHS and makes no volatile nor
+ aliased references. */
+
+static tree
+lookup_avail_expr (tree stmt, varray_type *block_avail_exprs_p, bool insert)
+{
+ void **slot;
+ tree lhs;
+ tree temp;
+ struct expr_hash_elt *element = xcalloc (sizeof (struct expr_hash_elt), 1);
+
+ lhs = TREE_CODE (stmt) == MODIFY_EXPR ? TREE_OPERAND (stmt, 0) : NULL;
+
+ initialize_hash_element (stmt, lhs, element);
+
+ /* Don't bother remembering constant assignments and copy operations.
+ Constants and copy operations are handled by the constant/copy propagator
+ in optimize_stmt. */
+ if (TREE_CODE (element->rhs) == SSA_NAME
+ || is_gimple_min_invariant (element->rhs))
+ {
+ free (element);
+ return NULL_TREE;
+ }
+
+ /* If this is an equality test against zero, see if we have recorded a
+ nonzero value for the variable in question. */
+ if ((TREE_CODE (element->rhs) == EQ_EXPR
+ || TREE_CODE (element->rhs) == NE_EXPR)
+ && TREE_CODE (TREE_OPERAND (element->rhs, 0)) == SSA_NAME
+ && integer_zerop (TREE_OPERAND (element->rhs, 1)))
+ {
+ int indx = SSA_NAME_VERSION (TREE_OPERAND (element->rhs, 0));
+
+ if (bitmap_bit_p (nonzero_vars, indx))
+ {
+ tree t = element->rhs;
+ free (element);
+
+ if (TREE_CODE (t) == EQ_EXPR)
+ return boolean_false_node;
+ else
+ return boolean_true_node;
+ }
+ }
+
+ /* Finally try to find the expression in the main expression hash table. */
+ slot = htab_find_slot_with_hash (avail_exprs, element, element->hash,
+ (insert ? INSERT : NO_INSERT));
+ if (slot == NULL)
+ {
+ free (element);
+ return NULL_TREE;
+ }
+
+ if (*slot == NULL)
+ {
+ *slot = (void *) element;
+ if (! *block_avail_exprs_p)
+ VARRAY_TREE_INIT (*block_avail_exprs_p, 20, "block_avail_exprs");
+ VARRAY_PUSH_TREE (*block_avail_exprs_p, stmt ? stmt : element->rhs);
+ return NULL_TREE;
+ }
+
+ /* Extract the LHS of the assignment so that it can be used as the current
+ definition of another variable. */
+ lhs = ((struct expr_hash_elt *)*slot)->lhs;
+
+ /* See if the LHS appears in the CONST_AND_COPIES table. If it does, then
+ use the value from the const_and_copies table. */
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ temp = get_value_for (lhs, const_and_copies);
+ if (temp)
+ lhs = temp;
+ }
+
+ free (element);
+ return lhs;
+}
+
+/* Given a condition COND, record into HI_P, LO_P and INVERTED_P the
+ range of values that result in the conditional having a true value.
+
+ Return true if we are successful in extracting a range from COND and
+ false if we are unsuccessful. */
+
+static bool
+extract_range_from_cond (tree cond, tree *hi_p, tree *lo_p, int *inverted_p)
+{
+ tree op1 = TREE_OPERAND (cond, 1);
+ tree high, low, type;
+ int inverted;
+
+ /* Experiments have shown that it's rarely, if ever useful to
+ record ranges for enumerations. Presumably this is due to
+ the fact that they're rarely used directly. They are typically
+ cast into an integer type and used that way. */
+ if (TREE_CODE (TREE_TYPE (op1)) != INTEGER_TYPE)
+ return 0;
+
+ type = TREE_TYPE (op1);
+
+ switch (TREE_CODE (cond))
+ {
+ case EQ_EXPR:
+ high = low = op1;
+ inverted = 0;
+ break;
+
+ case NE_EXPR:
+ high = low = op1;
+ inverted = 1;
+ break;
+
+ case GE_EXPR:
+ low = op1;
+ high = TYPE_MAX_VALUE (type);
+ inverted = 0;
+ break;
+
+ case GT_EXPR:
+ low = int_const_binop (PLUS_EXPR, op1, integer_one_node, 1);
+ high = TYPE_MAX_VALUE (type);
+ inverted = 0;
+ break;
+
+ case LE_EXPR:
+ high = op1;
+ low = TYPE_MIN_VALUE (type);
+ inverted = 0;
+ break;
+
+ case LT_EXPR:
+ high = int_const_binop (MINUS_EXPR, op1, integer_one_node, 1);
+ low = TYPE_MIN_VALUE (type);
+ inverted = 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ *hi_p = high;
+ *lo_p = low;
+ *inverted_p = inverted;
+ return 1;
+}
+
+/* Record a range created by COND for basic block BB. */
+
+static void
+record_range (tree cond, basic_block bb, varray_type *vrp_variables_p)
+{
+ /* We explicitly ignore NE_EXPRs. They rarely allow for meaningful
+ range optimizations and significantly complicate the implementation. */
+ if (TREE_CODE_CLASS (TREE_CODE (cond)) == '<'
+ && TREE_CODE (cond) != NE_EXPR
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (cond, 1))) == INTEGER_TYPE)
+ {
+ struct vrp_element *element = ggc_alloc (sizeof (struct vrp_element));
+ int ssa_version = SSA_NAME_VERSION (TREE_OPERAND (cond, 0));
+
+ varray_type *vrp_records_p
+ = (varray_type *)&VARRAY_GENERIC_PTR (vrp_data, ssa_version);
+
+ element->low = NULL;
+ element->high = NULL;
+ element->cond = cond;
+ element->bb = bb;
+
+ if (*vrp_records_p == NULL)
+ {
+ VARRAY_GENERIC_PTR_INIT (*vrp_records_p, 2, "vrp records");
+ VARRAY_GENERIC_PTR (vrp_data, ssa_version) = *vrp_records_p;
+ }
+
+ VARRAY_PUSH_GENERIC_PTR (*vrp_records_p, element);
+ if (! *vrp_variables_p)
+ VARRAY_TREE_INIT (*vrp_variables_p, 2, "vrp_variables");
+ VARRAY_PUSH_TREE (*vrp_variables_p, TREE_OPERAND (cond, 0));
+ }
+}
+
+/* Given a conditional statement IF_STMT, return the assignment 'X = Y'
+ known to be true depending on which arm of IF_STMT is taken.
+
+ Not all conditional statements will result in a useful assignment.
+ Return NULL_TREE in that case.
+
+ Also enter into the available expression table statements of
+ the form:
+
+ TRUE ARM FALSE ARM
+ 1 = cond 1 = cond'
+ 0 = cond' 0 = cond
+
+ This allows us to lookup the condition in a dominated block and
+ get back a constant indicating if the condition is true. */
+
+static struct eq_expr_value
+get_eq_expr_value (tree if_stmt,
+ int true_arm,
+ varray_type *block_avail_exprs_p,
+ basic_block bb,
+ varray_type *vrp_variables_p)
+{
+ tree cond;
+ struct eq_expr_value retval;
+
+ cond = COND_EXPR_COND (if_stmt);
+ retval.src = NULL;
+ retval.dst = NULL;
+
+ /* If the conditional is a single variable 'X', return 'X = 1' for
+ the true arm and 'X = 0' on the false arm. */
+ if (TREE_CODE (cond) == SSA_NAME)
+ {
+ retval.dst = cond;
+ retval.src = (true_arm ? integer_one_node : integer_zero_node);
+ return retval;
+ }
+
+ /* If we have a comparison expression, then record its result into
+ the available expression table. */
+ if (TREE_CODE_CLASS (TREE_CODE (cond)) == '<')
+ {
+ tree op0 = TREE_OPERAND (cond, 0);
+ tree op1 = TREE_OPERAND (cond, 1);
+
+ /* Special case comparing booleans against a constant as we know
+ the value of OP0 on both arms of the branch. ie, we can record
+ an equivalence for OP0 rather than COND. */
+ if ((TREE_CODE (cond) == EQ_EXPR || TREE_CODE (cond) == NE_EXPR)
+ && TREE_CODE (op0) == SSA_NAME
+ && TREE_CODE (TREE_TYPE (op0)) == BOOLEAN_TYPE
+ && is_gimple_min_invariant (op1))
+ {
+ if ((TREE_CODE (cond) == EQ_EXPR && true_arm)
+ || (TREE_CODE (cond) == NE_EXPR && ! true_arm))
+ {
+ retval.src = op1;
+ }
+ else
+ {
+ if (integer_zerop (op1))
+ retval.src = boolean_true_node;
+ else
+ retval.src = boolean_false_node;
+ }
+ retval.dst = op0;
+ return retval;
+ }
+
+ if (TREE_CODE (op0) == SSA_NAME
+ && (is_gimple_min_invariant (op1) || TREE_CODE (op1) == SSA_NAME))
+ {
+ tree inverted = invert_truthvalue (cond);
+
+ /* When we find an available expression in the hash table, we replace
+ the expression with the LHS of the statement in the hash table.
+
+ So, we want to build statements such as "1 = <condition>" on the
+ true arm and "0 = <condition>" on the false arm. That way if we
+ find the expression in the table, we will replace it with its
+ known constant value. Also insert inversions of the result and
+ condition into the hash table. */
+ if (true_arm)
+ {
+ record_cond (cond, boolean_true_node, block_avail_exprs_p);
+ record_cond (inverted, boolean_false_node, block_avail_exprs_p);
+
+ if (TREE_CONSTANT (op1))
+ record_range (cond, bb, vrp_variables_p);
+
+ /* If the conditional is of the form 'X == Y', return 'X = Y'
+ for the true arm. */
+ if (TREE_CODE (cond) == EQ_EXPR)
+ {
+ retval.dst = op0;
+ retval.src = op1;
+ return retval;
+ }
+ }
+ else
+ {
+
+ record_cond (inverted, boolean_true_node, block_avail_exprs_p);
+ record_cond (cond, boolean_false_node, block_avail_exprs_p);
+
+ if (TREE_CONSTANT (op1))
+ record_range (inverted, bb, vrp_variables_p);
+
+ /* If the conditional is of the form 'X != Y', return 'X = Y'
+ for the false arm. */
+ if (TREE_CODE (cond) == NE_EXPR)
+ {
+ retval.dst = op0;
+ retval.src = op1;
+ return retval;
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+/* Hashing and equality functions for AVAIL_EXPRS. The table stores
+ MODIFY_EXPR statements. We compute a value number for expressions using
+ the code of the expression and the SSA numbers of its operands. */
+
+static hashval_t
+avail_expr_hash (const void *p)
+{
+ stmt_ann_t ann = ((struct expr_hash_elt *)p)->ann;
+ tree rhs = ((struct expr_hash_elt *)p)->rhs;
+ hashval_t val = 0;
+ size_t i;
+ vuse_optype vuses;
+
+ /* iterative_hash_expr knows how to deal with any expression and
+ deals with commutative operators as well, so just use it instead
+ of duplicating such complexities here. */
+ val = iterative_hash_expr (rhs, val);
+
+ /* If the hash table entry is not associated with a statement, then we
+ can just hash the expression and not worry about virtual operands
+ and such. */
+ if (!ann)
+ return val;
+
+ /* Add the SSA version numbers of every vuse operand. This is important
+ because compound variables like arrays are not renamed in the
+ operands. Rather, the rename is done on the virtual variable
+ representing all the elements of the array. */
+ vuses = VUSE_OPS (ann);
+ for (i = 0; i < NUM_VUSES (vuses); i++)
+ val = iterative_hash_expr (VUSE_OP (vuses, i), val);
+
+ return val;
+}
+
+
+static int
+avail_expr_eq (const void *p1, const void *p2)
+{
+ stmt_ann_t ann1 = ((struct expr_hash_elt *)p1)->ann;
+ tree rhs1 = ((struct expr_hash_elt *)p1)->rhs;
+ stmt_ann_t ann2 = ((struct expr_hash_elt *)p2)->ann;
+ tree rhs2 = ((struct expr_hash_elt *)p2)->rhs;
+
+ /* If they are the same physical expression, return true. */
+ if (rhs1 == rhs2 && ann1 == ann2)
+ return true;
+
+ /* If their codes are not equal, then quit now. */
+ if (TREE_CODE (rhs1) != TREE_CODE (rhs2))
+ return false;
+
+ /* In case of a collision, both RHS have to be identical and have the
+ same VUSE operands. */
+ if ((TREE_TYPE (rhs1) == TREE_TYPE (rhs2)
+ || lang_hooks.types_compatible_p (TREE_TYPE (rhs1), TREE_TYPE (rhs2)))
+ && operand_equal_p (rhs1, rhs2, OEP_PURE_SAME))
+ {
+ vuse_optype ops1 = NULL;
+ vuse_optype ops2 = NULL;
+ size_t num_ops1 = 0;
+ size_t num_ops2 = 0;
+ size_t i;
+
+ if (ann1)
+ {
+ ops1 = VUSE_OPS (ann1);
+ num_ops1 = NUM_VUSES (ops1);
+ }
+
+ if (ann2)
+ {
+ ops2 = VUSE_OPS (ann2);
+ num_ops2 = NUM_VUSES (ops2);
+ }
+
+ /* If the number of virtual uses is different, then we consider
+ them not equal. */
+ if (num_ops1 != num_ops2)
+ return false;
+
+ for (i = 0; i < num_ops1; i++)
+ if (VUSE_OP (ops1, i) != VUSE_OP (ops2, i))
+ return false;
+
+#ifdef ENABLE_CHECKING
+ if (((struct expr_hash_elt *)p1)->hash
+ != ((struct expr_hash_elt *)p2)->hash)
+ abort ();
+#endif
+ return true;
+ }
+
+ return false;
+}
+
+/* Given STMT and a pointer to the block local defintions BLOCK_DEFS_P,
+ register register all objects set by this statement into BLOCK_DEFS_P
+ and CURRDEFS. */
+
+static void
+register_definitions_for_stmt (stmt_ann_t ann, varray_type *block_defs_p)
+{
+ def_optype defs;
+ vdef_optype vdefs;
+ unsigned int i;
+
+ defs = DEF_OPS (ann);
+ for (i = 0; i < NUM_DEFS (defs); i++)
+ {
+ tree def = DEF_OP (defs, i);
+
+ /* FIXME: We shouldn't be registering new defs if the variable
+ doesn't need to be renamed. */
+ register_new_def (def, block_defs_p);
+ }
+
+ /* Register new virtual definitions made by the statement. */
+ vdefs = VDEF_OPS (ann);
+ for (i = 0; i < NUM_VDEFS (vdefs); i++)
+ {
+ /* FIXME: We shouldn't be registering new defs if the variable
+ doesn't need to be renamed. */
+ register_new_def (VDEF_RESULT (vdefs, i), block_defs_p);
+ }
+}
+