aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.2.1-5666.3/gcc/tree-ssa-sink.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.2.1-5666.3/gcc/tree-ssa-sink.c')
-rw-r--r--gcc-4.2.1-5666.3/gcc/tree-ssa-sink.c576
1 files changed, 576 insertions, 0 deletions
diff --git a/gcc-4.2.1-5666.3/gcc/tree-ssa-sink.c b/gcc-4.2.1-5666.3/gcc/tree-ssa-sink.c
new file mode 100644
index 000000000..ad961afa2
--- /dev/null
+++ b/gcc-4.2.1-5666.3/gcc/tree-ssa-sink.c
@@ -0,0 +1,576 @@
+/* Code sinking for trees
+ Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Contributed by Daniel Berlin <dan@dberlin.org>
+
+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, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "ggc.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "diagnostic.h"
+#include "tree-inline.h"
+#include "tree-flow.h"
+#include "tree-gimple.h"
+#include "tree-dump.h"
+#include "timevar.h"
+#include "fibheap.h"
+#include "hashtab.h"
+#include "tree-iterator.h"
+#include "real.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "flags.h"
+#include "bitmap.h"
+#include "langhooks.h"
+#include "cfgloop.h"
+
+/* TODO:
+ 1. Sinking store only using scalar promotion (IE without moving the RHS):
+
+ *q = p;
+ p = p + 1;
+ if (something)
+ *q = <not p>;
+ else
+ y = *q;
+
+
+ should become
+ sinktemp = p;
+ p = p + 1;
+ if (something)
+ *q = <not p>;
+ else
+ {
+ *q = sinktemp;
+ y = *q
+ }
+ Store copy propagation will take care of the store elimination above.
+
+
+ 2. Sinking using Partial Dead Code Elimination. */
+
+
+static struct
+{
+ /* The number of statements sunk down the flowgraph by code sinking. */
+ int sunk;
+
+} sink_stats;
+
+
+/* Given a PHI, and one of its arguments (DEF), find the edge for
+ that argument and return it. If the argument occurs twice in the PHI node,
+ we return NULL. */
+
+static basic_block
+find_bb_for_arg (tree phi, tree def)
+{
+ int i;
+ bool foundone = false;
+ basic_block result = NULL;
+ for (i = 0; i < PHI_NUM_ARGS (phi); i++)
+ if (PHI_ARG_DEF (phi, i) == def)
+ {
+ if (foundone)
+ return NULL;
+ foundone = true;
+ result = PHI_ARG_EDGE (phi, i)->src;
+ }
+ return result;
+}
+
+/* When the first immediate use is in a statement, then return true if all
+ immediate uses in IMM are in the same statement.
+ We could also do the case where the first immediate use is in a phi node,
+ and all the other uses are in phis in the same basic block, but this
+ requires some expensive checking later (you have to make sure no def/vdef
+ in the statement occurs for multiple edges in the various phi nodes it's
+ used in, so that you only have one place you can sink it to. */
+
+static bool
+all_immediate_uses_same_place (tree stmt)
+{
+ tree firstuse = NULL_TREE;
+ ssa_op_iter op_iter;
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+ tree var;
+
+ FOR_EACH_SSA_TREE_OPERAND (var, stmt, op_iter, SSA_OP_ALL_DEFS)
+ {
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
+ {
+ if (firstuse == NULL_TREE)
+ firstuse = USE_STMT (use_p);
+ else
+ if (firstuse != USE_STMT (use_p))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Some global stores don't necessarily have V_MAY_DEF's of global variables,
+ but we still must avoid moving them around. */
+
+bool
+is_hidden_global_store (tree stmt)
+{
+ /* Check virtual definitions. If we get here, the only virtual
+ definitions we should see are those generated by assignment
+ statements. */
+ if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_VIRTUAL_DEFS))
+ {
+ tree lhs;
+
+ gcc_assert (TREE_CODE (stmt) == MODIFY_EXPR);
+
+ /* Note that we must not check the individual virtual operands
+ here. In particular, if this is an aliased store, we could
+ end up with something like the following (SSA notation
+ redacted for brevity):
+
+ foo (int *p, int i)
+ {
+ int x;
+ p_1 = (i_2 > 3) ? &x : p;
+
+ # x_4 = V_MAY_DEF <x_3>
+ *p_1 = 5;
+
+ return 2;
+ }
+
+ Notice that the store to '*p_1' should be preserved, if we
+ were to check the virtual definitions in that store, we would
+ not mark it needed. This is because 'x' is not a global
+ variable.
+
+ Therefore, we check the base address of the LHS. If the
+ address is a pointer, we check if its name tag or symbol tag is
+ a global variable. Otherwise, we check if the base variable
+ is a global. */
+ lhs = TREE_OPERAND (stmt, 0);
+ if (REFERENCE_CLASS_P (lhs))
+ lhs = get_base_address (lhs);
+
+ if (lhs == NULL_TREE)
+ {
+ /* If LHS is NULL, it means that we couldn't get the base
+ address of the reference. In which case, we should not
+ move this store. */
+ return true;
+ }
+ else if (DECL_P (lhs))
+ {
+ /* If the store is to a global symbol, we need to keep it. */
+ if (is_global_var (lhs))
+ return true;
+
+ }
+ else if (INDIRECT_REF_P (lhs))
+ {
+ tree ptr = TREE_OPERAND (lhs, 0);
+ struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
+ tree nmt = (pi) ? pi->name_mem_tag : NULL_TREE;
+ tree smt = var_ann (SSA_NAME_VAR (ptr))->symbol_mem_tag;
+
+ /* If either the name tag or the symbol tag for PTR is a
+ global variable, then the store is necessary. */
+ if ((nmt && is_global_var (nmt))
+ || (smt && is_global_var (smt)))
+ {
+ return true;
+ }
+ }
+ else
+ gcc_unreachable ();
+ }
+ return false;
+}
+
+/* Find the nearest common dominator of all of the immediate uses in IMM. */
+
+static basic_block
+nearest_common_dominator_of_uses (tree stmt)
+{
+ bitmap blocks = BITMAP_ALLOC (NULL);
+ basic_block commondom;
+ unsigned int j;
+ bitmap_iterator bi;
+ ssa_op_iter op_iter;
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+ tree var;
+
+ bitmap_clear (blocks);
+ FOR_EACH_SSA_TREE_OPERAND (var, stmt, op_iter, SSA_OP_ALL_DEFS)
+ {
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
+ {
+ tree usestmt = USE_STMT (use_p);
+ basic_block useblock;
+
+ if (TREE_CODE (usestmt) == PHI_NODE)
+ {
+ int idx = PHI_ARG_INDEX_FROM_USE (use_p);
+
+ useblock = PHI_ARG_EDGE (usestmt, idx)->src;
+ }
+ else
+ {
+ useblock = bb_for_stmt (usestmt);
+ }
+
+ /* Short circuit. Nothing dominates the entry block. */
+ if (useblock == ENTRY_BLOCK_PTR)
+ {
+ BITMAP_FREE (blocks);
+ return NULL;
+ }
+ bitmap_set_bit (blocks, useblock->index);
+ }
+ }
+ commondom = BASIC_BLOCK (bitmap_first_set_bit (blocks));
+ EXECUTE_IF_SET_IN_BITMAP (blocks, 0, j, bi)
+ commondom = nearest_common_dominator (CDI_DOMINATORS, commondom,
+ BASIC_BLOCK (j));
+ BITMAP_FREE (blocks);
+ return commondom;
+}
+
+/* Given a statement (STMT) and the basic block it is currently in (FROMBB),
+ determine the location to sink the statement to, if any.
+ Return the basic block to sink it to, or NULL if we should not sink
+ it. */
+
+static tree
+statement_sink_location (tree stmt, basic_block frombb)
+{
+ tree use, def;
+ use_operand_p one_use = NULL_USE_OPERAND_P;
+ basic_block sinkbb;
+ use_operand_p use_p;
+ def_operand_p def_p;
+ ssa_op_iter iter;
+ stmt_ann_t ann;
+ tree rhs;
+ imm_use_iterator imm_iter;
+
+ FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
+ {
+ FOR_EACH_IMM_USE_FAST (one_use, imm_iter, def)
+ {
+ break;
+ }
+ if (one_use != NULL_USE_OPERAND_P)
+ break;
+ }
+
+ /* Return if there are no immediate uses of this stmt. */
+ if (one_use == NULL_USE_OPERAND_P)
+ return NULL;
+
+ if (TREE_CODE (stmt) != MODIFY_EXPR)
+ return NULL;
+ rhs = TREE_OPERAND (stmt, 1);
+
+ /* There are a few classes of things we can't or don't move, some because we
+ don't have code to handle it, some because it's not profitable and some
+ because it's not legal.
+
+ We can't sink things that may be global stores, at least not without
+ calculating a lot more information, because we may cause it to no longer
+ be seen by an external routine that needs it depending on where it gets
+ moved to.
+
+ We don't want to sink loads from memory.
+
+ We can't sink statements that end basic blocks without splitting the
+ incoming edge for the sink location to place it there.
+
+ We can't sink statements that have volatile operands.
+
+ We don't want to sink dead code, so anything with 0 immediate uses is not
+ sunk.
+
+ */
+ ann = stmt_ann (stmt);
+ if (stmt_ends_bb_p (stmt)
+ || TREE_SIDE_EFFECTS (rhs)
+ || TREE_CODE (rhs) == EXC_PTR_EXPR
+ || TREE_CODE (rhs) == FILTER_EXPR
+ || is_hidden_global_store (stmt)
+ || ann->has_volatile_ops
+ || !ZERO_SSA_OPERANDS (stmt, SSA_OP_VUSE))
+ return NULL;
+
+ FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, iter, SSA_OP_ALL_DEFS)
+ {
+ tree def = DEF_FROM_PTR (def_p);
+ if (is_global_var (SSA_NAME_VAR (def))
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def))
+ return NULL;
+ }
+
+ FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
+ {
+ tree use = USE_FROM_PTR (use_p);
+ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use))
+ return NULL;
+ }
+
+ /* If all the immediate uses are not in the same place, find the nearest
+ common dominator of all the immediate uses. For PHI nodes, we have to
+ find the nearest common dominator of all of the predecessor blocks, since
+ that is where insertion would have to take place. */
+ if (!all_immediate_uses_same_place (stmt))
+ {
+ basic_block commondom = nearest_common_dominator_of_uses (stmt);
+
+ if (commondom == frombb)
+ return NULL;
+
+ /* Our common dominator has to be dominated by frombb in order to be a
+ trivially safe place to put this statement, since it has multiple
+ uses. */
+ if (!dominated_by_p (CDI_DOMINATORS, commondom, frombb))
+ return NULL;
+
+ /* It doesn't make sense to move to a dominator that post-dominates
+ frombb, because it means we've just moved it into a path that always
+ executes if frombb executes, instead of reducing the number of
+ executions . */
+ if (dominated_by_p (CDI_POST_DOMINATORS, frombb, commondom))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Not moving store, common dominator post-dominates from block.\n");
+ return NULL;
+ }
+
+ if (commondom == frombb || commondom->loop_depth > frombb->loop_depth)
+ return NULL;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Common dominator of all uses is %d\n",
+ commondom->index);
+ }
+ return first_stmt (commondom);
+ }
+
+ use = USE_STMT (one_use);
+ if (TREE_CODE (use) != PHI_NODE)
+ {
+ sinkbb = bb_for_stmt (use);
+ if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
+ || sinkbb->loop_father != frombb->loop_father)
+ return NULL;
+ return use;
+ }
+
+ /* Note that at this point, all uses must be in the same statement, so it
+ doesn't matter which def op we choose, pick the first one. */
+ FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
+ break;
+
+
+ sinkbb = find_bb_for_arg (use, def);
+ if (!sinkbb)
+ return NULL;
+
+ /* This will happen when you have
+ a_3 = PHI <a_13, a_26>
+
+ a_26 = V_MAY_DEF <a_3>
+
+ If the use is a phi, and is in the same bb as the def,
+ we can't sink it. */
+
+ if (bb_for_stmt (use) == frombb)
+ return NULL;
+ if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
+ || sinkbb->loop_father != frombb->loop_father)
+ return NULL;
+
+ return first_stmt (sinkbb);
+}
+
+/* Perform code sinking on BB */
+
+static void
+sink_code_in_bb (basic_block bb)
+{
+ basic_block son;
+ block_stmt_iterator bsi;
+ edge_iterator ei;
+ edge e;
+
+ /* If this block doesn't dominate anything, there can't be any place to sink
+ the statements to. */
+ if (first_dom_son (CDI_DOMINATORS, bb) == NULL)
+ goto earlyout;
+
+ /* We can't move things across abnormal edges, so don't try. */
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->flags & EDGE_ABNORMAL)
+ goto earlyout;
+
+ for (bsi = bsi_last (bb); !bsi_end_p (bsi);)
+ {
+ tree stmt = bsi_stmt (bsi);
+ block_stmt_iterator tobsi;
+ tree sinkstmt;
+
+ sinkstmt = statement_sink_location (stmt, bb);
+ if (!sinkstmt)
+ {
+ if (!bsi_end_p (bsi))
+ bsi_prev (&bsi);
+ continue;
+ }
+ if (dump_file)
+ {
+ fprintf (dump_file, "Sinking ");
+ print_generic_expr (dump_file, stmt, TDF_VOPS);
+ fprintf (dump_file, " from bb %d to bb %d\n",
+ bb->index, bb_for_stmt (sinkstmt)->index);
+ }
+ tobsi = bsi_for_stmt (sinkstmt);
+ /* Find the first non-label. */
+ while (!bsi_end_p (tobsi)
+ && TREE_CODE (bsi_stmt (tobsi)) == LABEL_EXPR)
+ bsi_next (&tobsi);
+
+ /* If this is the end of the basic block, we need to insert at the end
+ of the basic block. */
+ if (bsi_end_p (tobsi))
+ bsi_move_to_bb_end (&bsi, bb_for_stmt (sinkstmt));
+ else
+ bsi_move_before (&bsi, &tobsi);
+
+ sink_stats.sunk++;
+ if (!bsi_end_p (bsi))
+ bsi_prev (&bsi);
+
+ }
+ earlyout:
+ for (son = first_dom_son (CDI_POST_DOMINATORS, bb);
+ son;
+ son = next_dom_son (CDI_POST_DOMINATORS, son))
+ {
+ sink_code_in_bb (son);
+ }
+}
+
+/* Perform code sinking.
+ This moves code down the flowgraph when we know it would be
+ profitable to do so, or it wouldn't increase the number of
+ executions of the statement.
+
+ IE given
+
+ a_1 = b + c;
+ if (<something>)
+ {
+ }
+ else
+ {
+ foo (&b, &c);
+ a_5 = b + c;
+ }
+ a_6 = PHI (a_5, a_1);
+ USE a_6.
+
+ we'll transform this into:
+
+ if (<something>)
+ {
+ a_1 = b + c;
+ }
+ else
+ {
+ foo (&b, &c);
+ a_5 = b + c;
+ }
+ a_6 = PHI (a_5, a_1);
+ USE a_6.
+
+ Note that this reduces the number of computations of a = b + c to 1
+ when we take the else edge, instead of 2.
+*/
+static void
+execute_sink_code (void)
+{
+ struct loops *loops = loop_optimizer_init (LOOPS_NORMAL);
+
+ connect_infinite_loops_to_exit ();
+ memset (&sink_stats, 0, sizeof (sink_stats));
+ /* APPLE LOCAL begin 4708067 */
+ /* These are enums, not bitfields. */
+ calculate_dominance_info (CDI_DOMINATORS);
+ calculate_dominance_info (CDI_POST_DOMINATORS);
+ /* APPLE LOCAL end 4708067 */
+ sink_code_in_bb (EXIT_BLOCK_PTR);
+ if (dump_file && (dump_flags & TDF_STATS))
+ fprintf (dump_file, "Sunk statements:%d\n", sink_stats.sunk);
+ free_dominance_info (CDI_POST_DOMINATORS);
+ remove_fake_exit_edges ();
+ loop_optimizer_finalize (loops);
+}
+
+/* Gate and execute functions for PRE. */
+
+static unsigned int
+do_sink (void)
+{
+ execute_sink_code ();
+ return 0;
+}
+
+static bool
+gate_sink (void)
+{
+ return flag_tree_sink != 0;
+}
+
+struct tree_opt_pass pass_sink_code =
+{
+ "sink", /* name */
+ gate_sink, /* gate */
+ do_sink, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_SINK, /* tv_id */
+ PROP_no_crit_edges | PROP_cfg
+ | PROP_ssa | PROP_alias, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa
+ | TODO_dump_func
+ | TODO_ggc_collect
+ | TODO_verify_ssa, /* todo_flags_finish */
+ 0 /* letter */
+};