aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Law <law@gcc.gnu.org>2001-07-09 13:47:27 -0600
committerJeff Law <law@gcc.gnu.org>2001-07-09 13:47:27 -0600
commit44cec3004a3519037c88ae0d464f9790ac998053 (patch)
tree6a7040f8d80fe7ff4b7a087da4eb54d8dfcb9dbf
parent0b47e4c1cdabbb5e7bce3c0358fd82595be2fc60 (diff)
downloadgcc-44cec3004a3519037c88ae0d464f9790ac998053.zip
gcc-44cec3004a3519037c88ae0d464f9790ac998053.tar.gz
gcc-44cec3004a3519037c88ae0d464f9790ac998053.tar.bz2
Forgot to commit with last batch.
From-SVN: r43873
-rw-r--r--gcc/ssa-ccp.c1221
1 files changed, 1221 insertions, 0 deletions
diff --git a/gcc/ssa-ccp.c b/gcc/ssa-ccp.c
new file mode 100644
index 0000000..2316824
--- /dev/null
+++ b/gcc/ssa-ccp.c
@@ -0,0 +1,1221 @@
+/* Conditional constant propagation pass for the GNU compiler.
+ Copyright (C) 2000,2001 Free Software Foundation, Inc.
+ Original framework by Daniel Berlin <dan@cgsoftware.com>
+ Fleshed out and major cleanups by Jeff Law <law@redhat.com>
+
+This file is part of GNU CC.
+
+GNU CC 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.
+
+GNU CC 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 GNU CC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* Conditional constant propagation.
+
+ References:
+
+ Constant propagation with conditional branches,
+ Wegman and Zadeck, ACM TOPLAS 13(2):181-210.
+
+ Building an Optimizing Compiler,
+ Robert Morgan, Butterworth-Heinemann, 1998, Section 8.9.
+
+ Advanced Compiler Design and Implementation,
+ Steven Muchnick, Morgan Kaufmann, 1997, Section 12.6
+
+ The overall structure is as follows:
+
+ 1. Run a simple SSA based DCE pass to remove any dead code.
+ 2. Run CCP to compute what registers are known constants
+ and what edges are not executable. Remove unexecutable
+ edges from the CFG and simplify PHI nodes.
+ 3. Replace registers with constants where possible.
+ 4. Remove unreachable blocks computed in step #2.
+ 5. Another simple SSA DCE pass to remove dead code exposed
+ by CCP.
+
+ When we exit, we are still in SSA form.
+
+
+ Potential further enhancements:
+
+ 1. Handle SUBREGs, STRICT_LOW_PART, etc in destinations more
+ gracefully.
+
+ 2. Handle insns with multiple outputs more gracefully.
+
+ 3. Handle CONST_DOUBLE and symbolic constants.
+
+ 4. Fold expressions after performing constant substitutions. */
+
+
+#include "config.h"
+#include "system.h"
+
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "ssa.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+#include "errors.h"
+#include "ggc.h"
+#include "df.h"
+#include "function.h"
+
+/* Possible lattice values. */
+
+typedef enum
+{
+ UNDEFINED,
+ CONSTANT,
+ VARYING
+} latticevalue;
+
+/* Main structure for CCP.
+
+ Contains the lattice value and, if it's a constant, the constant
+ value. */
+typedef struct
+{
+ latticevalue lattice_val;
+ rtx const_value;
+} value;
+
+/* Array of values indexed by register number. */
+static value *values;
+
+/* A bitmap to keep track of executable blocks in the CFG. */
+static sbitmap executable_blocks;
+
+/* A bitmap for all executable edges in the CFG. */
+static sbitmap executable_edges;
+
+/* Array of edges on the work list. */
+static edge *edge_info;
+
+/* We need an edge list to be able to get indexes easily. */
+static struct edge_list *edges;
+
+/* For building/following use-def and def-use chains. */
+static struct df *df_analyzer;
+
+/* Current edge we are operating on, from the worklist */
+static edge flow_edges;
+
+/* Bitmap of SSA edges which will need reexamination as their definition
+ has changed. */
+static sbitmap ssa_edges;
+
+/* Simple macros to simplify code */
+#define SSA_NAME(x) REGNO (SET_DEST (x))
+#define PHI_PARMS(x) XVEC (SET_SRC (x), 0)
+#define EIE(x,y) EDGE_INDEX (edges, x, y)
+
+rtx first_phi_node PARAMS ((basic_block));
+static void visit_phi_node PARAMS ((rtx, basic_block));
+static void visit_expression PARAMS ((rtx, basic_block));
+static void defs_to_undefined PARAMS ((rtx));
+static void defs_to_varying PARAMS ((rtx));
+static void examine_flow_edges PARAMS ((void));
+static void follow_def_use_chains PARAMS ((void));
+static void optimize_unexecutable_edges PARAMS ((struct edge_list *, sbitmap));
+static void ssa_ccp_substitute_constants PARAMS ((void));
+static void ssa_ccp_df_delete_unreachable_insns PARAMS ((void));
+
+/* Return the first PHI node in a basic block. This routine knows
+ what INSNs can start a basic block and what can validly follow
+ them up to the first PHI node.
+
+ If the INSN chain or block structures are incorrect, then the behavior
+ of this routine is undefined. verify_flow_info will normally catch
+ these problems in a more graceful manner. */
+rtx
+first_phi_node (block)
+ basic_block block;
+{
+ rtx insn = block->head;
+
+ /* Eat the optional CODE_LABEL at the start of the block. */
+ if (GET_CODE (insn) == CODE_LABEL)
+ insn = NEXT_INSN (insn);
+
+ /* Eat the mandatory NOTE_INSN_BASIC_BLOCK. */
+ if (!NOTE_INSN_BASIC_BLOCK_P (insn) || NOTE_BASIC_BLOCK (insn) != block)
+ abort ();
+
+ /* If there is a PHI node in this block, then it will be the next insn. */
+ return NEXT_INSN (insn);
+}
+
+/* Loop through the PHI_NODE's parameters for BLOCK and compare their
+ lattice values to determine PHI_NODE's lattice value. */
+static void
+visit_phi_node (phi_node, block)
+ rtx phi_node;
+ basic_block block;
+{
+ unsigned int i;
+ rtx phi_node_expr = NULL;
+ unsigned int phi_node_name = SSA_NAME (PATTERN (phi_node));
+ latticevalue phi_node_lattice_val = UNDEFINED;
+ rtx pat = PATTERN (phi_node);
+ rtvec phi_vec = XVEC (SET_SRC (pat), 0);
+ unsigned int num_elem = GET_NUM_ELEM (phi_vec);
+
+ for (i = 0; i < num_elem; i += 2)
+ {
+ if (TEST_BIT (executable_edges,
+ EIE (BASIC_BLOCK (INTVAL (RTVEC_ELT (phi_vec, i + 1))),
+ block)))
+ {
+ unsigned int current_parm
+ = REGNO (RTVEC_ELT (phi_vec, i));
+
+ latticevalue current_parm_lattice_val
+ = values[current_parm].lattice_val;
+
+ /* If any node is VARYING, then new value of PHI_NODE
+ is VARYING. */
+ if (current_parm_lattice_val == VARYING)
+ {
+ phi_node_lattice_val = VARYING;
+ phi_node_expr = NULL;
+ break;
+ }
+
+ /* If we have more than one distinct constant, then the new
+ value of PHI_NODE is VARYING. */
+ if (current_parm_lattice_val == CONSTANT
+ && phi_node_lattice_val == CONSTANT
+ && values[current_parm].const_value != phi_node_expr)
+ {
+ phi_node_lattice_val = VARYING;
+ phi_node_expr = NULL;
+ break;
+ }
+
+ /* If the current value of PHI_NODE is UNDEFINED and one
+ node in PHI_NODE is CONSTANT, then the new value of the
+ PHI is that CONSTANT. Note this can turn into VARYING
+ if we find another distinct constant later. */
+ if (phi_node_lattice_val == UNDEFINED
+ && phi_node_expr == NULL
+ && current_parm_lattice_val == CONSTANT)
+ {
+ phi_node_expr = values[current_parm].const_value;
+ phi_node_lattice_val = CONSTANT;
+ continue;
+ }
+ }
+ }
+
+ /* If the value of PHI_NODE changed, then we will need to
+ re-execute uses of the output of PHI_NODE. */
+ if (phi_node_lattice_val != values[phi_node_name].lattice_val)
+ {
+ values[phi_node_name].lattice_val = phi_node_lattice_val;
+ values[phi_node_name].const_value = phi_node_expr;
+ SET_BIT (ssa_edges, phi_node_name);
+ }
+}
+
+/* Sets all defs in an insn to UNDEFINED. */
+static void
+defs_to_undefined (insn)
+ rtx insn;
+{
+ struct df_link *currdef;
+ for (currdef = DF_INSN_DEFS (df_analyzer, insn); currdef;
+ currdef = currdef->next)
+ {
+ if (values[DF_REF_REGNO (currdef->ref)].lattice_val != UNDEFINED)
+ SET_BIT (ssa_edges, DF_REF_REGNO (currdef->ref));
+ values[DF_REF_REGNO (currdef->ref)].lattice_val = UNDEFINED;
+ }
+}
+
+/* Sets all defs in an insn to VARYING. */
+static void
+defs_to_varying (insn)
+ rtx insn;
+{
+ struct df_link *currdef;
+ for (currdef = DF_INSN_DEFS (df_analyzer, insn); currdef;
+ currdef = currdef->next)
+ {
+ if (values[DF_REF_REGNO (currdef->ref)].lattice_val != VARYING)
+ SET_BIT (ssa_edges, DF_REF_REGNO (currdef->ref));
+ values[DF_REF_REGNO (currdef->ref)].lattice_val = VARYING;
+ }
+}
+
+/* Go through the expression, call the approriate evaluation routines
+ to attempt cprop */
+static void
+visit_expression (insn, block)
+ rtx insn;
+ basic_block block;
+{
+ rtx src, dest, set;
+
+ set = single_set (insn);
+ if (! set)
+ {
+ defs_to_varying (insn);
+ return;
+ }
+
+ src = SET_SRC (set);
+ dest = SET_DEST (set);
+
+ /* We may want to refine this some day. */
+ if (GET_CODE (dest) != REG && dest != pc_rtx)
+ {
+ defs_to_varying (insn);
+ return;
+ }
+
+ /* Hard registers are not put in SSA form and thus we must consider
+ them varying. All the more reason to avoid hard registers in
+ RTL until as late as possible in the compilation. */
+ if (GET_CODE (dest) == REG && REGNO (dest) < FIRST_PSEUDO_REGISTER)
+ {
+ defs_to_varying (insn);
+ return;
+ }
+
+ /* If this is assigning DEST to a constant, record that fact. */
+ if (GET_CODE (src) == CONST_INT && GET_CODE (insn) == INSN)
+ {
+ unsigned int resultreg = REGNO (dest);
+
+ values[resultreg].lattice_val = CONSTANT;
+ values[resultreg].const_value = SET_SRC (PATTERN (insn));
+ SET_BIT (ssa_edges, resultreg);
+ }
+
+ /* If this is a copy operation, then we can copy the lattice values. */
+ else if (GET_CODE (src) == REG && GET_CODE (dest) == REG)
+ {
+ unsigned int old_value = REGNO (src);
+ latticevalue old_lattice_value = values[old_value].lattice_val;
+ unsigned int new_value = REGNO (dest);
+
+ /* Unless the lattice value is going to change, don't bother
+ adding the "new value" into the worklist. */
+ if (values[new_value].lattice_val != old_lattice_value
+ || values[new_value].const_value != values[old_value].const_value)
+ SET_BIT (ssa_edges, new_value);
+
+ /* Copy the old lattice node info into the new value lattice node. */
+ values[new_value].lattice_val = old_lattice_value;
+ values[new_value].const_value = values[old_value].const_value;
+ }
+
+ /* Handle jumps. */
+ else if (GET_CODE (insn) == JUMP_INSN)
+ {
+ rtx x = pc_set (insn);
+ if (GET_CODE (src) != IF_THEN_ELSE)
+ {
+ edge curredge;
+
+ /* This is a computed jump, table jump, or an unconditional
+ jump. For all these cases we want to mark all successor
+ blocks as executable if they have not already been
+ marked.
+
+ One day we may try do better with swtich tables and
+ other computed jumps. */
+ for (curredge = block->succ; curredge;
+ curredge = curredge->succ_next)
+ {
+ int index = EIE (curredge->src, curredge->dest);
+
+ if (TEST_BIT (executable_edges, index))
+ continue;
+
+ SET_BIT (executable_edges, index);
+ edge_info[index] = flow_edges;
+ flow_edges = curredge;
+ }
+ }
+ else
+ {
+ edge curredge;
+ enum rtx_code comparison_code;
+ rtx comparison_src0;
+ rtx comparison_src1;
+
+ comparison_code = GET_CODE (XEXP (src, 0));
+ comparison_src0 = XEXP (XEXP (src, 0), 0);
+ comparison_src1 = XEXP (XEXP (src, 0), 1);
+
+ /* If either operand is undefined, then there is nothing to
+ do right now. If/when operands are later defined we will
+ revaluate the condition and take the appropriate action. */
+ if ((GET_CODE (comparison_src0) == REG
+ && values[REGNO (comparison_src0)].lattice_val == UNDEFINED)
+ || (GET_CODE (comparison_src1) == REG
+ && values[REGNO (comparison_src1)].lattice_val == UNDEFINED))
+ return;
+
+ /* If either operand is varying, then we must consider all
+ paths as executable. */
+ if ((GET_CODE (comparison_src0) == REG
+ && values[REGNO (comparison_src0)].lattice_val == VARYING)
+ || (GET_CODE (comparison_src1) == REG
+ && values[REGNO (comparison_src1)].lattice_val == VARYING))
+ {
+ for (curredge = block->succ; curredge;
+ curredge = curredge->succ_next)
+ {
+ int index = EIE (curredge->src, curredge->dest);
+
+ if (TEST_BIT (executable_edges, index))
+ continue;
+
+ SET_BIT (executable_edges, index);
+ edge_info[index] = flow_edges;
+ flow_edges = curredge;
+ }
+ return;
+ }
+
+ /* Try to simplify the comparison. */
+ if (GET_CODE (comparison_src0) == REG
+ && values[REGNO (comparison_src0)].lattice_val == CONSTANT)
+ comparison_src0 = values[REGNO (comparison_src0)].const_value;
+
+ if (GET_CODE (comparison_src1) == REG
+ && values[REGNO (comparison_src1)].lattice_val == CONSTANT)
+ comparison_src1 = values[REGNO (comparison_src1)].const_value;
+
+ x = simplify_ternary_operation (IF_THEN_ELSE,
+ VOIDmode,
+ GET_MODE (XEXP (src, 0)),
+ gen_rtx (comparison_code,
+ GET_MODE (XEXP (src, 0)),
+ comparison_src0,
+ comparison_src1),
+ XEXP (src, 1),
+ XEXP (src, 2));
+
+ /* Walk through all the outgoing edges from this block and see
+ which (if any) we should mark as executable. */
+ for (curredge = block->succ; curredge;
+ curredge = curredge->succ_next)
+ {
+ int index = EIE (curredge->src, curredge->dest);
+
+ if (TEST_BIT (executable_edges, index))
+ continue;
+
+ /* If we were unable to simplify the expression at this
+ point, it's highly unlikely we'll be able to simplify
+ it later. So consider all edges as executable if the
+ expression did not simplify.
+
+ If the expression simplified to (pc), then we know we
+ will take the fall-thru edge, so mark it. Similarly,
+ if the expression simplified to (label_ref ...), then
+ we know the branch will be taken and we mark that
+ edge as taken. */
+ if (!x
+ || (x == pc_rtx
+ && (curredge->flags & EDGE_FALLTHRU))
+ || (GET_CODE (x) == LABEL_REF
+ && ! (curredge->flags & EDGE_FALLTHRU)))
+ {
+ SET_BIT (executable_edges, index);
+ edge_info[index] = flow_edges;
+ flow_edges = curredge;
+ }
+ }
+ }
+ }
+ else if (!PHI_NODE_P (insn))
+ {
+ rtx simplified = NULL;
+
+ /* We've got some kind of INSN. If it's simple, try to evaluate
+ it and record the results.
+
+ We already know this insn is a single_set and that it sets
+ a pseudo register. So we just need to extract the source
+ arguments, simplify them to constants if possible, then
+ simplify the expression as a whole if possible. */
+ switch (GET_RTX_CLASS (GET_CODE (src)))
+ {
+ case '<':
+ {
+ rtx src0 = XEXP (src, 0);
+ rtx src1 = XEXP (src, 1);
+ enum machine_mode mode;
+
+ /* If either is undefined, then the result is undefined. */
+ if ((GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == UNDEFINED)
+ || (GET_CODE (src1) == REG
+ && values[REGNO (src1)].lattice_val == UNDEFINED))
+ {
+ defs_to_undefined (insn);
+ break;
+ }
+
+ /* Simplify source operands to whatever known values they
+ may have. */
+ if (GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == CONSTANT)
+ src0 = values[REGNO (src0)].const_value;
+
+ if (GET_CODE (src1) == REG
+ && values[REGNO (src1)].lattice_val == CONSTANT)
+ src1 = values[REGNO (src1)].const_value;
+
+ /* See if the simplifier can determine if this operation
+ computes a constant value. */
+ mode = GET_MODE (src0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (src1);
+
+ simplified = simplify_relational_operation (GET_CODE (src),
+ mode, src0, src1);
+ break;
+
+ }
+
+ case '1':
+ {
+ rtx src0 = XEXP (src, 0);
+
+ /* If the operand is undefined, then the result is undefined. */
+ if (GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == UNDEFINED)
+ {
+ defs_to_undefined (insn);
+ break;
+ }
+
+ /* Simplify source operands to whatever known values they
+ may have. */
+ if (GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == CONSTANT)
+ src0 = values[REGNO (src0)].const_value;
+
+ /* See if the simplifier can determine if this operation
+ computes a constant value. */
+ simplified = simplify_unary_operation (GET_CODE (src),
+ GET_MODE (src),
+ src0,
+ GET_MODE (src0));
+ break;
+ }
+
+ case '2':
+ case 'c':
+ {
+ rtx src0 = XEXP (src, 0);
+ rtx src1 = XEXP (src, 1);
+
+ /* If either is undefined, then the result is undefined. */
+ if ((GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == UNDEFINED)
+ || (GET_CODE (src1) == REG
+ && values[REGNO (src1)].lattice_val == UNDEFINED))
+ {
+ defs_to_undefined (insn);
+ break;
+ }
+
+ /* Simplify source operands to whatever known values they
+ may have. */
+ if (GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == CONSTANT)
+ src0 = values[REGNO (src0)].const_value;
+
+ if (GET_CODE (src1) == REG
+ && values[REGNO (src1)].lattice_val == CONSTANT)
+ src1 = values[REGNO (src1)].const_value;
+
+ /* See if the simplifier can determine if this operation
+ computes a constant value. */
+ simplified = simplify_binary_operation (GET_CODE (src),
+ GET_MODE (src),
+ src0, src1);
+ break;
+ }
+
+ case '3':
+ case 'b':
+ {
+ rtx src0 = XEXP (src, 0);
+ rtx src1 = XEXP (src, 1);
+ rtx src2 = XEXP (src, 2);
+
+ /* If either is undefined, then the result is undefined. */
+ if ((GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == UNDEFINED)
+ || (GET_CODE (src1) == REG
+ && values[REGNO (src1)].lattice_val == UNDEFINED)
+ || (GET_CODE (src2) == REG
+ && values[REGNO (src2)].lattice_val == UNDEFINED))
+ {
+ defs_to_undefined (insn);
+ break;
+ }
+
+ /* Simplify source operands to whatever known values they
+ may have. */
+ if (GET_CODE (src0) == REG
+ && values[REGNO (src0)].lattice_val == CONSTANT)
+ src0 = values[REGNO (src0)].const_value;
+
+ if (GET_CODE (src1) == REG
+ && values[REGNO (src1)].lattice_val == CONSTANT)
+ src1 = values[REGNO (src1)].const_value;
+
+ if (GET_CODE (src2) == REG
+ && values[REGNO (src2)].lattice_val == CONSTANT)
+ src2 = values[REGNO (src2)].const_value;
+
+ /* See if the simplifier can determine if this operation
+ computes a constant value. */
+ simplified = simplify_ternary_operation (GET_CODE (src),
+ GET_MODE (src),
+ GET_MODE (src),
+ src0, src1, src2);
+ break;
+ }
+
+ default:
+ defs_to_varying (insn);
+ }
+
+ if (simplified && GET_CODE (simplified) == CONST_INT)
+ {
+ if (values[REGNO (dest)].lattice_val != CONSTANT
+ || values[REGNO (dest)].const_value != simplified)
+ SET_BIT (ssa_edges, REGNO (dest));
+
+ values[REGNO (dest)].lattice_val = CONSTANT;
+ values[REGNO (dest)].const_value = simplified;
+ }
+ else
+ defs_to_varying (insn);
+ }
+}
+
+/* Iterate over the FLOW_EDGES work list. Simulate the target block
+ for each edge. */
+static void
+examine_flow_edges (void)
+{
+ while (flow_edges != NULL)
+ {
+ basic_block succ_block;
+ rtx curr_phi_node;
+
+ /* Pull the next block to simulate off the worklist. */
+ succ_block = flow_edges->dest;
+ flow_edges = edge_info[EIE (flow_edges->src, flow_edges->dest)];
+
+ /* There is nothing to do for the exit block. */
+ if (succ_block == EXIT_BLOCK_PTR)
+ continue;
+
+ /* Always simulate PHI nodes, even if we have simulated this block
+ before. Note that all PHI nodes are consecutive within a block. */
+ for (curr_phi_node = first_phi_node (succ_block);
+ PHI_NODE_P (curr_phi_node);
+ curr_phi_node = NEXT_INSN (curr_phi_node))
+ visit_phi_node (curr_phi_node, succ_block);
+
+ /* If this is the first time we've simulated this block, then we
+ must simulate each of its insns. */
+ if (!TEST_BIT (executable_blocks, succ_block->index))
+ {
+ rtx currinsn;
+ edge succ_edge = succ_block->succ;
+
+ /* Note that we have simulated this block. */
+ SET_BIT (executable_blocks, succ_block->index);
+
+ /* Simulate each insn within the block. */
+ currinsn = succ_block->head;
+ while (currinsn != succ_block->end)
+ {
+ if (INSN_P (currinsn))
+ visit_expression (currinsn, succ_block);
+
+ currinsn = NEXT_INSN (currinsn);
+ }
+
+ /* Don't forget the last insn in the block. */
+ if (INSN_P (currinsn))
+ visit_expression (currinsn, succ_block);
+
+ /* If we haven't looked at the next block, and it has a
+ single successor, add it onto the worklist. This is because
+ if we only have one successor, we know it gets executed,
+ so we don't have to wait for cprop to tell us. */
+ if (succ_edge != NULL
+ && succ_edge->succ_next == NULL
+ && !TEST_BIT (executable_edges,
+ EIE (succ_edge->src, succ_edge->dest)))
+ {
+ SET_BIT (executable_edges,
+ EIE (succ_edge->src, succ_edge->dest));
+ edge_info[EIE (succ_edge->src, succ_edge->dest)] = flow_edges;
+ flow_edges = succ_edge;
+ }
+ }
+ }
+}
+
+/* Follow the def-use chains for each definition on the worklist and
+ simulate the uses of the definition. */
+
+static void
+follow_def_use_chains ()
+{
+ /* Iterate over all the entries on the SSA_EDGES worklist. */
+ while (sbitmap_first_set_bit (ssa_edges) >= 0)
+ {
+ int member;
+ struct df_link *curruse;
+
+ /* Pick an entry off the worklist (it does not matter which
+ entry we pick). */
+ member = sbitmap_first_set_bit (ssa_edges);
+ RESET_BIT (ssa_edges, member);
+
+ /* Iterate through all the uses of this entry. */
+ for (curruse = df_analyzer->regs[member].uses; curruse;
+ curruse = curruse->next)
+ {
+ rtx useinsn;
+
+ useinsn = DF_REF_INSN (curruse->ref);
+ if (PHI_NODE_P (useinsn))
+ {
+ if (TEST_BIT (executable_blocks, BLOCK_NUM (useinsn)))
+ visit_phi_node (useinsn, BLOCK_FOR_INSN (useinsn));
+ }
+ else
+ {
+ if (TEST_BIT (executable_blocks, BLOCK_NUM (useinsn)))
+ visit_expression (useinsn, BLOCK_FOR_INSN (useinsn));
+ }
+ }
+ }
+}
+
+/* Examine each edge to see if we were able to prove any were
+ not executable.
+
+ If an edge is not executable, then we can remove its alternative
+ in PHI nodes as the destination of the edge, we can simplify the
+ conditional branch at the source of the edge, and we can remove
+ the edge from the CFG. Note we do not delete unreachable blocks
+ yet as the DF analyzer can not deal with that yet. */
+static void
+optimize_unexecutable_edges (edges, executable_edges)
+ struct edge_list *edges;
+ sbitmap executable_edges;
+{
+ int i;
+
+ for (i = 0; i < NUM_EDGES (edges); i++)
+ {
+ if (!TEST_BIT (executable_edges, i))
+ {
+ edge edge = INDEX_EDGE (edges, i);
+
+ if (edge->flags & EDGE_ABNORMAL)
+ continue;
+
+ /* We found an edge that is not executable. First simplify
+ the PHI nodes in the target block. */
+ if (edge->dest != EXIT_BLOCK_PTR)
+ {
+ rtx insn = first_phi_node (edge->dest);
+
+ while (PHI_NODE_P (insn))
+ {
+ remove_phi_alternative (PATTERN (insn), edge->src);
+ insn = NEXT_INSN (insn);
+ }
+ }
+
+ /* Since the edge was not executable, remove it from the CFG. */
+ remove_edge (edge);
+ }
+ }
+
+ /* We have removed all the unexecutable edges from the CFG. Fix up
+ the conditional jumps at the end of any affected block.
+
+ We have three cases to deal with:
+
+ a. Both outgoing edges are not executable. This happens if the
+ source block is not reachable. We will deal with this by
+ deleting all the insns in the block later.
+
+ b. The fall-thru edge is not executable. In this case we
+ change the conditional jump into an unconditional jump and
+ add a BARRIER after the unconditional jump. Note that since
+ we are working on generic RTL we can change the jump in-place
+ instead of dealing with the headache of reemitting the jump.
+
+ c. The branch taken edge is not executable. In this case
+ we turn the jump into (set (pc) (pc)) which is a nop-jump
+ and we will remove the unrecognizable insn later.
+
+ In cases B & C we are removing uses of registers, so make sure
+ to note those changes for the DF analyzer. */
+
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ basic_block bb = BASIC_BLOCK (i);
+ rtx insn = bb->end;
+ edge edge = bb->succ;
+
+ /* If we have no predecessors, then this block is unreachable and
+ will be cleaned up when we remove unreachable blocks. */
+ if (bb->pred == NULL || GET_CODE (insn) != JUMP_INSN)
+ continue;
+
+ /* If this block ends in a conditional jump, but only has one
+ successor, then the jump needs adjustment. */
+ if (condjump_p (insn) && ! simplejump_p (insn)
+ && bb->succ && bb->succ->succ_next == NULL)
+ {
+ /* If the fallthru edge is the executable edge, then turn
+ this jump into a nop jump, otherwise make it an unconditinoal
+ jump to its target. */
+ if (edge->flags & EDGE_FALLTHRU)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ }
+ else
+ {
+ SET_SRC (PATTERN (insn)) = gen_rtx_LABEL_REF (Pmode,
+ JUMP_LABEL (insn));
+ emit_barrier_after (insn);
+ INSN_CODE (insn) = -1;
+ }
+
+ /* Inform the DF analyzer that this insn changed. */
+ df_insn_modify (df_analyzer, BLOCK_FOR_INSN (insn), insn);
+ }
+ }
+}
+
+/* Perform substitution of known values for pseudo registers.
+
+ ??? Note we do not do simplifications or constant folding here, it
+ is unlikely that any significant simplifications can be done here
+ anyway. Consider that if the simplification would result in an
+ expression that produces a constant value that the value would
+ have been discovered and recorded already.
+
+ We perform two transformations. First, we initialize pseudos to their
+ known constant values at their definition point. Second, we try to
+ replace uses with the known constant value. */
+
+static void
+ssa_ccp_substitute_constants ()
+{
+ int i;
+
+ for (i = FIRST_PSEUDO_REGISTER; i < VARRAY_SIZE (ssa_definition); i++)
+ {
+ if (values[i].lattice_val == CONSTANT)
+ {
+ rtx def = VARRAY_RTX (ssa_definition, i);
+ rtx set = single_set (def);
+ struct df_link *curruse;
+
+ if (! set)
+ continue;
+
+ /* Do not try to simplify PHI nodes down to a constant load.
+ That will be done later as we translate out of SSA. Also,
+ doing that here could violate the rule that all PHI nodes
+ are consecutive at the start of the basic block. */
+ if (! PHI_NODE_P (def))
+ {
+ SET_SRC (set) = values[i].const_value;
+ INSN_CODE (def) = -1;
+ df_insn_modify (df_analyzer, BLOCK_FOR_INSN (def), def);
+ }
+
+ /* Iterate through all the uses of this entry and try replacements
+ there too. Note it is not particularly profitable to try
+ and fold/simplify expressions here as most of the common
+ cases were handled above. */
+ for (curruse = df_analyzer->regs[i].uses;
+ curruse;
+ curruse = curruse->next)
+ {
+ rtx useinsn;
+
+ useinsn = DF_REF_INSN (curruse->ref);
+
+ if (!INSN_DELETED_P (useinsn)
+ && ! (GET_CODE (useinsn) == NOTE
+ && NOTE_LINE_NUMBER (useinsn) == NOTE_INSN_DELETED)
+ && (GET_CODE (useinsn) == INSN
+ || GET_CODE (useinsn) == JUMP_INSN))
+ {
+ validate_replace_src (regno_reg_rtx [i],
+ values[i].const_value,
+ useinsn);
+ INSN_CODE (useinsn) = -1;
+ df_insn_modify (df_analyzer,
+ BLOCK_FOR_INSN (useinsn),
+ useinsn);
+ }
+
+ }
+ }
+ }
+}
+
+/* Now find all unreachable basic blocks. All the insns in those
+ blocks are unreachable, so delete them and mark any necessary
+ updates for the DF analyzer. */
+
+static void
+ssa_ccp_df_delete_unreachable_insns ()
+{
+ int i;
+
+ /* Use the CFG to find all the reachable blocks. */
+ find_unreachable_blocks ();
+
+ /* Now we know what blocks are not reachable. Mark all the insns
+ in those blocks as deleted for the DF analyzer. We'll let the
+ normal flow code actually remove the unreachable blocks. */
+ for (i = n_basic_blocks - 1; i >= 0; --i)
+ {
+ basic_block b = BASIC_BLOCK (i);
+
+ if (b->aux != NULL)
+ /* This block was found. Tidy up the mark. */
+ b->aux = NULL;
+ else
+ {
+ rtx start = b->head;
+ rtx end = b->end;
+ rtx tmp;
+
+ /* Include any jump table following the basic block. */
+ end = b->end;
+ if (GET_CODE (end) == JUMP_INSN
+ && (tmp = JUMP_LABEL (end)) != NULL_RTX
+ && (tmp = NEXT_INSN (tmp)) != NULL_RTX
+ && GET_CODE (tmp) == JUMP_INSN
+ && (GET_CODE (PATTERN (tmp)) == ADDR_VEC
+ || GET_CODE (PATTERN (tmp)) == ADDR_DIFF_VEC))
+ end = tmp;
+
+ while (1)
+ {
+ rtx next = NEXT_INSN (start);
+
+ if (GET_CODE (start) == INSN
+ || GET_CODE (start) == CALL_INSN
+ || GET_CODE (start) == JUMP_INSN)
+ df_insn_delete (df_analyzer, BLOCK_FOR_INSN (start), start);
+
+ if (start == end)
+ break;
+ start = next;
+ }
+ }
+ }
+}
+
+
+/* Main entry point for SSA Conditional Constant Propagation.
+
+ Long term it should accept as input the specific flow graph to
+ operate on so that it can be called for sub-graphs. */
+
+void
+ssa_const_prop (void)
+{
+ unsigned int i;
+ edge curredge;
+
+ /* We need alias analysis (for what?) */
+ init_alias_analysis ();
+
+ df_analyzer = df_init ();
+ df_analyse (df_analyzer, 0,
+ DF_RD_CHAIN | DF_RU_CHAIN | DF_REG_INFO | DF_HARD_REGS);
+
+ /* We need mappings from insn to its containing block. */
+ compute_bb_for_insn (get_max_uid ());
+
+ /* Perform a quick and dirty dead code elimination pass. This is not
+ as aggressive as it could be, but it's good enough to clean up a
+ lot of unwanted junk and it is fast. */
+ ssa_fast_dce (df_analyzer);
+
+ /* Build an edge list from the CFG. */
+ edges = create_edge_list ();
+
+ /* Initialize the values array with everything as undefined. */
+ values = (value *) xmalloc (VARRAY_SIZE (ssa_definition) * sizeof (value));
+ for (i = 0; i < VARRAY_SIZE (ssa_definition); i++)
+ {
+ if (i < FIRST_PSEUDO_REGISTER)
+ values[i].lattice_val = VARYING;
+ else
+ values[i].lattice_val = UNDEFINED;
+ values[i].const_value = NULL;
+ }
+
+ ssa_edges = sbitmap_alloc (VARRAY_SIZE (ssa_definition));
+ sbitmap_zero (ssa_edges);
+
+ executable_blocks = sbitmap_alloc (n_basic_blocks);
+ sbitmap_zero (executable_blocks);
+
+ executable_edges = sbitmap_alloc (NUM_EDGES (edges));
+ sbitmap_zero (executable_edges);
+
+ edge_info = (edge *) xmalloc (NUM_EDGES (edges) * sizeof (edge));
+ flow_edges = ENTRY_BLOCK_PTR->succ;
+
+ /* Add the successors of the entry block to the edge worklist. That
+ is enough of a seed to get SSA-CCP started. */
+ for (curredge = ENTRY_BLOCK_PTR->succ; curredge;
+ curredge = curredge->succ_next)
+ {
+ int index = EIE (curredge->src, curredge->dest);
+ SET_BIT (executable_edges, index);
+ edge_info[index] = curredge->succ_next;
+ }
+
+ /* Iterate until until the worklists are empty. */
+ do
+ {
+ examine_flow_edges ();
+ follow_def_use_chains ();
+ }
+ while (flow_edges != NULL);
+
+ /* Now perform substitutions based on the known constant values. */
+ ssa_ccp_substitute_constants ();
+
+ /* Remove unexecutable edges from the CFG and make appropriate
+ adjustments to PHI nodes. */
+ optimize_unexecutable_edges (edges, executable_edges);
+
+ /* Now remove all unreachable insns and update the DF information.
+ as appropriate. */
+ ssa_ccp_df_delete_unreachable_insns ();
+
+#if 0
+ /* The DF analyzer expects the number of blocks to remain constant,
+ so we can't remove unreachable blocks.
+
+ Code the DF analyzer calls expects there to be no unreachable
+ blocks in the CFG. So we can't leave unreachable blocks in the
+ CFG.
+
+ So, there is no way to do an incremental update of the DF data
+ at this point. */
+ df_analyse (df_analyzer, 0,
+ DF_RD_CHAIN | DF_RU_CHAIN | DF_REG_INFO | DF_HARD_REGS);
+#endif
+
+ /* Clean up any dead code exposed by SSA-CCP, do this after updating
+ the dataflow information! */
+ ssa_fast_dce (df_analyzer);
+
+ free (values);
+ values = NULL;
+
+ free (edge_info);
+ edge_info = NULL;
+
+ sbitmap_free (executable_blocks);
+ executable_blocks = NULL;
+
+ free_edge_list (edges);
+ edges = NULL;
+
+ sbitmap_free (executable_edges);
+ executable_edges = NULL;
+
+ df_finish (df_analyzer);
+ end_alias_analysis ();
+}
+
+static int
+mark_references (current_rtx, data)
+ rtx *current_rtx;
+ void *data;
+{
+ rtx x = *current_rtx;
+ sbitmap worklist = (sbitmap)data;
+
+ if (x == NULL_RTX)
+ return 0;
+
+ if (GET_CODE (x) == SET)
+ {
+ rtx dest = SET_DEST (x);
+
+ if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ {
+ rtx reg;
+
+ reg = dest;
+
+ while (GET_CODE (reg) == STRICT_LOW_PART
+ || GET_CODE (reg) == SUBREG
+ || GET_CODE (reg) == SIGN_EXTRACT
+ || GET_CODE (reg) == ZERO_EXTRACT)
+ reg = XEXP (reg, 0);
+
+ if (GET_CODE (reg) == REG)
+ SET_BIT (worklist, REGNO (reg));
+ }
+
+ if (GET_CODE (dest) == REG)
+ {
+ for_each_rtx (&SET_SRC (x), mark_references, data);
+ return -1;
+ }
+
+ return 0;
+ }
+ else if (GET_CODE (x) == REG)
+ {
+ SET_BIT (worklist, REGNO (x));
+ return -1;
+ }
+ else if (GET_CODE (x) == CLOBBER)
+ return -1;
+ else
+ return 0;
+}
+
+static void
+ssa_fast_dce (df)
+ struct df *df;
+{
+ sbitmap worklist = sbitmap_alloc (VARRAY_SIZE (ssa_definition));
+ sbitmap_ones (worklist);
+
+ /* Iterate on the worklist until there's no definitions left to
+ examine. */
+ while (sbitmap_first_set_bit (worklist) >= 0)
+ {
+ struct df_link *curruse;
+ int reg, found_use;
+
+ /* Remove an item from the worklist. */
+ reg = sbitmap_first_set_bit (worklist);
+ RESET_BIT (worklist, reg);
+
+ /* We never consider deleting assignments to hard regs or things
+ which do not have SSA definitions, or things we have already
+ deleted, or things with unusual side effects. */
+ if (reg < FIRST_PSEUDO_REGISTER
+ || ! VARRAY_RTX (ssa_definition, reg)
+ || INSN_DELETED_P (VARRAY_RTX (ssa_definition, reg))
+ || (GET_CODE (VARRAY_RTX (ssa_definition, reg)) == NOTE
+ && (NOTE_LINE_NUMBER (VARRAY_RTX (ssa_definition, reg))
+ == NOTE_INSN_DELETED))
+ || side_effects_p (PATTERN (VARRAY_RTX (ssa_definition, reg))))
+ continue;
+
+ /* Iterate over the uses of this register. If we can not find
+ any uses that have not been deleted, then the definition of
+ this register is dead. */
+ found_use = 0;
+ for (curruse = df->regs[reg].uses; curruse; curruse = curruse->next)
+ {
+ rtx useinsn;
+
+ if (curruse->ref
+ && DF_REF_INSN (curruse->ref)
+ && ! INSN_DELETED_P (DF_REF_INSN (curruse->ref))
+ && ! (GET_CODE (DF_REF_INSN (curruse->ref)) == NOTE
+ && (NOTE_LINE_NUMBER (DF_REF_INSN (curruse->ref))
+ == NOTE_INSN_DELETED))
+ && DF_REF_INSN (curruse->ref) != VARRAY_RTX (ssa_definition, reg))
+ {
+ found_use = 1;
+ break;
+ }
+ }
+
+ /* If we did not find a use of this register, then the definition
+ of this register is dead. */
+
+ if (! found_use)
+ {
+ rtx def = VARRAY_RTX (ssa_definition, reg);
+
+ /* Add all registers referenced by INSN to the work
+ list. */
+ for_each_rtx (&PATTERN (def), mark_references, worklist);
+
+ /* Inform the analyzer that this insn is going to be
+ deleted. */
+ df_insn_delete (df, BLOCK_FOR_INSN (def), def);
+
+ if (PHI_NODE_P (def))
+ {
+ if (def == BLOCK_FOR_INSN (def)->head
+ && def == BLOCK_FOR_INSN (def)->end)
+ {
+ /* Delete it. */
+ PUT_CODE (def, NOTE);
+ NOTE_LINE_NUMBER (def) = NOTE_INSN_DELETED;
+ }
+ else if (def == BLOCK_FOR_INSN (def)->head)
+ {
+ BLOCK_FOR_INSN (def)->head = NEXT_INSN (def);
+ flow_delete_insn (def);
+ }
+ else if (def == BLOCK_FOR_INSN (def)->end)
+ {
+ BLOCK_FOR_INSN (def)->end = PREV_INSN (def);
+ flow_delete_insn (def);
+ }
+ else
+ flow_delete_insn (def);
+ }
+ else
+ {
+ flow_delete_insn (def);
+ }
+ VARRAY_RTX (ssa_definition, reg) = NULL;
+ }
+ }
+}