summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-propagate.c
diff options
context:
space:
mode:
authordnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-08-29 06:16:02 +0000
committerdnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-08-29 06:16:02 +0000
commit415115853a731f3e3b2a5fa4f722dbe0377992d4 (patch)
tree7614a76a2ddfc4ba7fe4532833225adde4aeb47d /gcc/tree-ssa-propagate.c
parentb62788519dd2f174195164057a479f1353217524 (diff)
downloadgcc-415115853a731f3e3b2a5fa4f722dbe0377992d4.tar.gz
* Makefile.in (OBJS-common): Add tree-ssa-propagate.o
(tree-ssa-propagate.o): New rule. (GTFILES): Add tree-ssa-propagate.c. * tree-flow.h (struct stmt_ann_d): Remove field in_ccp_worklist. * tree-ssa-propagate.c: New file. * tree-ssa-propagate.h: New file. * tree-ssa-ccp.c: Re-write to use the routines from tree-ssa-propagate.c. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@86711 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-ssa-propagate.c')
-rw-r--r--gcc/tree-ssa-propagate.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/gcc/tree-ssa-propagate.c b/gcc/tree-ssa-propagate.c
new file mode 100644
index 00000000000..1577a9c0b5a
--- /dev/null
+++ b/gcc/tree-ssa-propagate.c
@@ -0,0 +1,674 @@
+/* Generic SSA value propagation engine.
+ Copyright (C) 2000, 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 "tree-pass.h"
+#include "tree-ssa-propagate.h"
+#include "langhooks.h"
+
+
+/* This file implements a generic value propagation engine based on
+ the same propagation used by the SSA-CCP algorithm [1].
+
+ Propagation is performed by simulating the execution of every
+ statement that produces the value being propagated. Simulation
+ proceeds as follows:
+
+ 1- Initially, all edges of the CFG are marked not executable and
+ the CFG worklist seeded with all the statements in the entry
+ basic block (block 0).
+
+ 2- Every statement S is simulated with a call to the call-back
+ function SSA_PROP_VISIT_STMT. This evaluation may produce 3
+ results:
+
+ SSA_PROP_NOT_INTERESTING: Statement S produces nothing of
+ interest and does not affect any of the work lists.
+
+ SSA_PROP_VARYING: The value produced by S cannot be determined
+ at compile time. Further simulation of S is not required.
+ If S is a conditional jump, all the outgoing edges for the
+ block are considered executable and added to the work
+ list.
+
+ SSA_PROP_INTERESTING: S produces a value that can be computed
+ at compile time. Its result can be propagated into the
+ statements that feed from S. Furhtermore, if S is a
+ conditional jump, only the edge known to be taken is added
+ to the work list. Edges that are known not to execute are
+ never simulated.
+
+ 3- PHI nodes are simulated with a call to SSA_PROP_VISIT_PHI. The
+ return value from SSA_PROP_VISIT_PHI has the same semantics as
+ described in #3.
+
+ 4- Three work lists are kept. Statements are only added to these
+ lists if they produce one of SSA_PROP_INTERESTING or
+ SSA_PROP_VARYING.
+
+ CFG_BLOCKS contains the list of blocks to be simulated.
+ Blocks are added to this list if their incoming edges are
+ found executable.
+
+ VARYING_SSA_EDGES contains the list of statements that feed
+ from statements that produce an SSA_PROP_VARYING result.
+ These are simulated first to speed up processing.
+
+ INTERESTING_SSA_EDGES contains the list of statements that
+ feed from statements that produce an SSA_PROP_INTERESTING
+ result.
+
+ 5- Simulation terminates when all three work lists are drained.
+
+ Before calling ssa_propagate, it is important to clear
+ DONT_SIMULATE_AGAIN for all the statements in the program that
+ should be simulated. This initialization allows an implementation
+ to specify which statements should never be simulated.
+
+ It is also important to compute def-use information before calling
+ ssa_propagate.
+
+ References:
+
+ [1] Constant propagation with conditional branches,
+ Wegman and Zadeck, ACM TOPLAS 13(2):181-210.
+
+ [2] Building an Optimizing Compiler,
+ Robert Morgan, Butterworth-Heinemann, 1998, Section 8.9.
+
+ [3] Advanced Compiler Design and Implementation,
+ Steven Muchnick, Morgan Kaufmann, 1997, Section 12.6 */
+
+/* Function pointers used to parameterize the propagation engine. */
+static ssa_prop_visit_stmt_fn ssa_prop_visit_stmt;
+static ssa_prop_visit_phi_fn ssa_prop_visit_phi;
+
+/* Use the TREE_DEPRECATED bitflag to mark statements that have been
+ added to one of the SSA edges worklists. This flag is used to
+ avoid visiting statements unnecessarily when draining an SSA edge
+ worklist. If while simulating a basic block, we find a statement with
+ STMT_IN_SSA_EDGE_WORKLIST set, we clear it to prevent SSA edge
+ processing from visiting it again. */
+#define STMT_IN_SSA_EDGE_WORKLIST(T) TREE_DEPRECATED (T)
+
+/* A bitmap to keep track of executable blocks in the CFG. */
+static sbitmap executable_blocks;
+
+/* Array of control flow edges on the worklist. */
+static GTY(()) varray_type cfg_blocks = NULL;
+
+static unsigned int cfg_blocks_num = 0;
+static int cfg_blocks_tail;
+static int cfg_blocks_head;
+
+static sbitmap bb_in_list;
+
+/* Worklist of SSA edges which will need reexamination as their
+ definition has changed. SSA edges are def-use edges in the SSA
+ web. For each D-U edge, we store the target statement or PHI node
+ U. */
+static GTY(()) varray_type interesting_ssa_edges;
+
+/* Identical to INTERESTING_SSA_EDGES. For performance reasons, the
+ list of SSA edges is split into two. One contains all SSA edges
+ who need to be reexamined because their lattice value changed to
+ varying (this worklist), and the other contains all other SSA edges
+ to be reexamined (INTERESTING_SSA_EDGES).
+
+ Since most values in the program are VARYING, the ideal situation
+ is to move them to that lattice value as quickly as possible.
+ Thus, it doesn't make sense to process any other type of lattice
+ value until all VARYING values are propagated fully, which is one
+ thing using the VARYING worklist achieves. In addition, if we
+ don't use a separate worklist for VARYING edges, we end up with
+ situations where lattice values move from
+ UNDEFINED->INTERESTING->VARYING instead of UNDEFINED->VARYING. */
+static GTY(()) varray_type varying_ssa_edges;
+
+
+/* Return true if the block worklist empty. */
+
+static inline bool
+cfg_blocks_empty_p (void)
+{
+ return (cfg_blocks_num == 0);
+}
+
+
+/* Add a basic block to the worklist. */
+
+static void
+cfg_blocks_add (basic_block bb)
+{
+ if (bb == ENTRY_BLOCK_PTR || bb == EXIT_BLOCK_PTR)
+ return;
+
+ if (TEST_BIT (bb_in_list, bb->index))
+ return;
+
+ if (cfg_blocks_empty_p ())
+ {
+ cfg_blocks_tail = cfg_blocks_head = 0;
+ cfg_blocks_num = 1;
+ }
+ else
+ {
+ cfg_blocks_num++;
+ if (cfg_blocks_num > VARRAY_SIZE (cfg_blocks))
+ {
+ /* We have to grow the array now. Adjust to queue to occupy the
+ full space of the original array. */
+ cfg_blocks_tail = VARRAY_SIZE (cfg_blocks);
+ cfg_blocks_head = 0;
+ VARRAY_GROW (cfg_blocks, 2 * VARRAY_SIZE (cfg_blocks));
+ }
+ else
+ cfg_blocks_tail = (cfg_blocks_tail + 1) % VARRAY_SIZE (cfg_blocks);
+ }
+
+ VARRAY_BB (cfg_blocks, cfg_blocks_tail) = bb;
+ SET_BIT (bb_in_list, bb->index);
+}
+
+
+/* Remove a block from the worklist. */
+
+static basic_block
+cfg_blocks_get (void)
+{
+ basic_block bb;
+
+ bb = VARRAY_BB (cfg_blocks, cfg_blocks_head);
+
+#ifdef ENABLE_CHECKING
+ if (cfg_blocks_empty_p () || !bb)
+ abort ();
+#endif
+
+ cfg_blocks_head = (cfg_blocks_head + 1) % VARRAY_SIZE (cfg_blocks);
+ --cfg_blocks_num;
+ RESET_BIT (bb_in_list, bb->index);
+
+ return bb;
+}
+
+
+/* We have just defined a new value for VAR. If IS_VARYING is true,
+ add all immediate uses of VAR to VARYING_SSA_EDGES, otherwise add
+ them to INTERESTING_SSA_EDGES. */
+
+static void
+add_ssa_edge (tree var, bool is_varying)
+{
+ tree stmt = SSA_NAME_DEF_STMT (var);
+ dataflow_t df = get_immediate_uses (stmt);
+ int num_uses = num_immediate_uses (df);
+ int i;
+
+ for (i = 0; i < num_uses; i++)
+ {
+ tree use_stmt = immediate_use (df, i);
+
+ if (!DONT_SIMULATE_AGAIN (use_stmt)
+ && !STMT_IN_SSA_EDGE_WORKLIST (use_stmt))
+ {
+ STMT_IN_SSA_EDGE_WORKLIST (use_stmt) = 1;
+ if (is_varying)
+ VARRAY_PUSH_TREE (varying_ssa_edges, use_stmt);
+ else
+ VARRAY_PUSH_TREE (interesting_ssa_edges, use_stmt);
+ }
+ }
+}
+
+
+/* Add edge E to the control flow worklist. */
+
+static void
+add_control_edge (edge e)
+{
+ basic_block bb = e->dest;
+ if (bb == EXIT_BLOCK_PTR)
+ return;
+
+ /* If the edge had already been executed, skip it. */
+ if (e->flags & EDGE_EXECUTABLE)
+ return;
+
+ e->flags |= EDGE_EXECUTABLE;
+
+ /* If the block is already in the list, we're done. */
+ if (TEST_BIT (bb_in_list, bb->index))
+ return;
+
+ cfg_blocks_add (bb);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Adding Destination of edge (%d -> %d) to worklist\n\n",
+ e->src->index, e->dest->index);
+}
+
+
+/* Simulate the execution of STMT and update the work lists accordingly. */
+
+static void
+simulate_stmt (tree stmt)
+{
+ enum ssa_prop_result val = SSA_PROP_NOT_INTERESTING;
+ edge taken_edge = NULL;
+ tree output_name = NULL_TREE;
+
+ /* Don't bother visiting statements that are already
+ considered varying by the propagator. */
+ if (DONT_SIMULATE_AGAIN (stmt))
+ return;
+
+ if (TREE_CODE (stmt) == PHI_NODE)
+ {
+ val = ssa_prop_visit_phi (stmt);
+ output_name = PHI_RESULT (stmt);
+ }
+ else
+ val = ssa_prop_visit_stmt (stmt, &taken_edge, &output_name);
+
+ if (val == SSA_PROP_VARYING)
+ {
+ DONT_SIMULATE_AGAIN (stmt) = 1;
+
+ /* If the statement produced a new varying value, add the SSA
+ edges coming out of OUTPUT_NAME. */
+ if (output_name)
+ add_ssa_edge (output_name, true);
+
+ /* If STMT transfers control out of its basic block, add
+ all outgoing edges to the work list. */
+ if (stmt_ends_bb_p (stmt))
+ {
+ edge e;
+ basic_block bb = bb_for_stmt (stmt);
+ for (e = bb->succ; e; e = e->succ_next)
+ add_control_edge (e);
+ }
+ }
+ else if (val == SSA_PROP_INTERESTING)
+ {
+ /* If the statement produced new value, add the SSA edges coming
+ out of OUTPUT_NAME. */
+ if (output_name)
+ add_ssa_edge (output_name, false);
+
+ /* If we know which edge is going to be taken out of this block,
+ add it to the CFG work list. */
+ if (taken_edge)
+ add_control_edge (taken_edge);
+ }
+}
+
+/* Process an SSA edge worklist. WORKLIST is the SSA edge worklist to
+ drain. This pops statements off the given WORKLIST and processes
+ them until there are no more statements on WORKLIST. */
+
+static void
+process_ssa_edge_worklist (varray_type *worklist)
+{
+ /* Drain the entire worklist. */
+ while (VARRAY_ACTIVE_SIZE (*worklist) > 0)
+ {
+ basic_block bb;
+
+ /* Pull the statement to simulate off the worklist. */
+ tree stmt = VARRAY_TOP_TREE (*worklist);
+ VARRAY_POP (*worklist);
+
+ /* If this statement was already visited by simulate_block, then
+ we don't need to visit it again here. */
+ if (!STMT_IN_SSA_EDGE_WORKLIST (stmt))
+ continue;
+
+ /* STMT is no longer in a worklist. */
+ STMT_IN_SSA_EDGE_WORKLIST (stmt) = 0;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "\nSimulating statement (from ssa_edges): ");
+ print_generic_stmt (dump_file, stmt, dump_flags);
+ }
+
+ bb = bb_for_stmt (stmt);
+
+ /* PHI nodes are always visited, regardless of whether or not
+ the destination block is executable. Otherwise, visit the
+ statement only if its block is marked executable. */
+ if (TREE_CODE (stmt) == PHI_NODE
+ || TEST_BIT (executable_blocks, bb->index))
+ simulate_stmt (stmt);
+ }
+}
+
+
+/* Simulate the execution of BLOCK. Evaluate the statement associated
+ with each variable reference inside the block. */
+
+static void
+simulate_block (basic_block block)
+{
+ tree phi;
+
+ /* There is nothing to do for the exit block. */
+ if (block == EXIT_BLOCK_PTR)
+ return;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nSimulating block %d\n", block->index);
+
+ /* Always simulate PHI nodes, even if we have simulated this block
+ before. */
+ for (phi = phi_nodes (block); phi; phi = PHI_CHAIN (phi))
+ simulate_stmt (phi);
+
+ /* If this is the first time we've simulated this block, then we
+ must simulate each of its statements. */
+ if (!TEST_BIT (executable_blocks, block->index))
+ {
+ block_stmt_iterator j;
+ unsigned int normal_edge_count;
+ edge e, normal_edge;
+
+ /* Note that we have simulated this block. */
+ SET_BIT (executable_blocks, block->index);
+
+ for (j = bsi_start (block); !bsi_end_p (j); bsi_next (&j))
+ {
+ tree stmt = bsi_stmt (j);
+
+ /* If this statement is already in the worklist then
+ "cancel" it. The reevaluation implied by the worklist
+ entry will produce the same value we generate here and
+ thus reevaluating it again from the worklist is
+ pointless. */
+ if (STMT_IN_SSA_EDGE_WORKLIST (stmt))
+ STMT_IN_SSA_EDGE_WORKLIST (stmt) = 0;
+
+ simulate_stmt (stmt);
+ }
+
+ /* We can not predict when abnormal edges will be executed, so
+ once a block is considered executable, we consider any
+ outgoing abnormal edges as executable.
+
+ At the same time, if this block has only one successor that is
+ reached by non-abnormal edges, then add that successor to the
+ worklist. */
+ normal_edge_count = 0;
+ normal_edge = NULL;
+ for (e = block->succ; e; e = e->succ_next)
+ {
+ if (e->flags & EDGE_ABNORMAL)
+ add_control_edge (e);
+ else
+ {
+ normal_edge_count++;
+ normal_edge = e;
+ }
+ }
+
+ if (normal_edge_count == 1)
+ add_control_edge (normal_edge);
+ }
+}
+
+
+/* Initialize local data structures and work lists. */
+
+static void
+ssa_prop_init (void)
+{
+ edge e;
+ basic_block bb;
+
+ /* Worklists of SSA edges. */
+ VARRAY_TREE_INIT (interesting_ssa_edges, 20, "interesting_ssa_edges");
+ VARRAY_TREE_INIT (varying_ssa_edges, 20, "varying_ssa_edges");
+
+ executable_blocks = sbitmap_alloc (last_basic_block);
+ sbitmap_zero (executable_blocks);
+
+ bb_in_list = sbitmap_alloc (last_basic_block);
+ sbitmap_zero (bb_in_list);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_immediate_uses (dump_file);
+
+ VARRAY_BB_INIT (cfg_blocks, 20, "cfg_blocks");
+
+ /* Initially assume that every edge in the CFG is not executable. */
+ FOR_EACH_BB (bb)
+ {
+ block_stmt_iterator si;
+
+ for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+ STMT_IN_SSA_EDGE_WORKLIST (bsi_stmt (si)) = 0;
+
+ for (e = bb->succ; e; e = e->succ_next)
+ e->flags &= ~EDGE_EXECUTABLE;
+ }
+
+ /* Seed the algorithm by adding the successors of the entry block to the
+ edge worklist. */
+ for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next)
+ {
+ if (e->dest != EXIT_BLOCK_PTR)
+ {
+ e->flags |= EDGE_EXECUTABLE;
+ cfg_blocks_add (e->dest);
+ }
+ }
+}
+
+
+/* Free allocated storage. */
+
+static void
+ssa_prop_fini (void)
+{
+ interesting_ssa_edges = NULL;
+ varying_ssa_edges = NULL;
+ cfg_blocks = NULL;
+ sbitmap_free (bb_in_list);
+ sbitmap_free (executable_blocks);
+ free_df ();
+}
+
+
+/* Get the main expression from statement STMT. */
+
+tree
+get_rhs (tree stmt)
+{
+ enum tree_code code = TREE_CODE (stmt);
+
+ switch (code)
+ {
+ case RETURN_EXPR:
+ stmt = TREE_OPERAND (stmt, 0);
+ if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
+ return stmt;
+ /* FALLTHRU */
+
+ case MODIFY_EXPR:
+ stmt = TREE_OPERAND (stmt, 1);
+ if (TREE_CODE (stmt) == WITH_SIZE_EXPR)
+ return TREE_OPERAND (stmt, 0);
+ else
+ return stmt;
+
+ case COND_EXPR:
+ return COND_EXPR_COND (stmt);
+ case SWITCH_EXPR:
+ return SWITCH_COND (stmt);
+ case GOTO_EXPR:
+ return GOTO_DESTINATION (stmt);
+ case LABEL_EXPR:
+ return LABEL_EXPR_LABEL (stmt);
+
+ default:
+ return stmt;
+ }
+}
+
+
+/* Set the main expression of *STMT_P to EXPR. If EXPR is not a valid
+ GIMPLE expression no changes are done and the function returns
+ false. */
+
+bool
+set_rhs (tree *stmt_p, tree expr)
+{
+ tree stmt = *stmt_p, op;
+ enum tree_code code = TREE_CODE (expr);
+ stmt_ann_t ann;
+ tree var;
+ ssa_op_iter iter;
+
+ /* Verify the constant folded result is valid gimple. */
+ if (TREE_CODE_CLASS (code) == '2')
+ {
+ if (!is_gimple_val (TREE_OPERAND (expr, 0))
+ || !is_gimple_val (TREE_OPERAND (expr, 1)))
+ return false;
+ }
+ else if (TREE_CODE_CLASS (code) == '1')
+ {
+ if (!is_gimple_val (TREE_OPERAND (expr, 0)))
+ return false;
+ }
+
+ switch (TREE_CODE (stmt))
+ {
+ case RETURN_EXPR:
+ op = TREE_OPERAND (stmt, 0);
+ if (TREE_CODE (op) != MODIFY_EXPR)
+ {
+ TREE_OPERAND (stmt, 0) = expr;
+ break;
+ }
+ stmt = op;
+ /* FALLTHRU */
+
+ case MODIFY_EXPR:
+ op = TREE_OPERAND (stmt, 1);
+ if (TREE_CODE (op) == WITH_SIZE_EXPR)
+ stmt = op;
+ TREE_OPERAND (stmt, 1) = expr;
+ break;
+
+ case COND_EXPR:
+ COND_EXPR_COND (stmt) = expr;
+ break;
+ case SWITCH_EXPR:
+ SWITCH_COND (stmt) = expr;
+ break;
+ case GOTO_EXPR:
+ GOTO_DESTINATION (stmt) = expr;
+ break;
+ case LABEL_EXPR:
+ LABEL_EXPR_LABEL (stmt) = expr;
+ break;
+
+ default:
+ /* Replace the whole statement with EXPR. If EXPR has no side
+ effects, then replace *STMT_P with an empty statement. */
+ ann = stmt_ann (stmt);
+ *stmt_p = TREE_SIDE_EFFECTS (expr) ? expr : build_empty_stmt ();
+ (*stmt_p)->common.ann = (tree_ann_t) ann;
+
+ if (TREE_SIDE_EFFECTS (expr))
+ {
+ /* Fix all the SSA_NAMEs created by *STMT_P to point to its new
+ replacement. */
+ FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_ALL_DEFS)
+ {
+ if (TREE_CODE (var) == SSA_NAME)
+ SSA_NAME_DEF_STMT (var) = *stmt_p;
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+/* Entry point to the propagation engine.
+
+ VISIT_STMT is called for every statement visited.
+ VISIT_PHI is called for every PHI node visited. */
+
+void
+ssa_propagate (ssa_prop_visit_stmt_fn visit_stmt,
+ ssa_prop_visit_phi_fn visit_phi)
+{
+ ssa_prop_visit_stmt = visit_stmt;
+ ssa_prop_visit_phi = visit_phi;
+
+ ssa_prop_init ();
+
+ /* Iterate until the worklists are empty. */
+ while (!cfg_blocks_empty_p ()
+ || VARRAY_ACTIVE_SIZE (interesting_ssa_edges) > 0
+ || VARRAY_ACTIVE_SIZE (varying_ssa_edges) > 0)
+ {
+ if (!cfg_blocks_empty_p ())
+ {
+ /* Pull the next block to simulate off the worklist. */
+ basic_block dest_block = cfg_blocks_get ();
+ simulate_block (dest_block);
+ }
+
+ /* In order to move things to varying as quickly as
+ possible,process the VARYING_SSA_EDGES worklist first. */
+ process_ssa_edge_worklist (&varying_ssa_edges);
+
+ /* Now process the INTERESTING_SSA_EDGES worklist. */
+ process_ssa_edge_worklist (&interesting_ssa_edges);
+ }
+
+ ssa_prop_fini ();
+}
+
+#include "gt-tree-ssa-propagate.h"