2002-02-01 18:16:02 +00:00
|
|
|
|
/* Conditional constant propagation pass for the GNU compiler.
|
|
|
|
|
Copyright (C) 2000, 2001, 2002 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 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. */
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
|
|
|
|
|
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 int mark_references PARAMS ((rtx *, 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));
|
|
|
|
|
static void ssa_fast_dce PARAMS ((struct df *));
|
|
|
|
|
|
|
|
|
|
/* 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 appropriate evaluation routines
|
|
|
|
|
to attempt cprop */
|
|
|
|
|
static void
|
|
|
|
|
visit_expression (insn, block)
|
|
|
|
|
rtx insn;
|
|
|
|
|
basic_block block;
|
|
|
|
|
{
|
|
|
|
|
rtx src, dest, set;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Ugh. CALL_INSNs may end a basic block and have multiple edges
|
|
|
|
|
leading out from them.
|
|
|
|
|
|
|
|
|
|
Mark all the outgoing edges as executable, then fall into the
|
|
|
|
|
normal processing below. */
|
|
|
|
|
if (GET_CODE (insn) == CALL_INSN && block->end == insn)
|
|
|
|
|
{
|
|
|
|
|
edge curredge;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the mode for the operation before we simplify
|
|
|
|
|
our arguments to constants. */
|
|
|
|
|
mode = GET_MODE (src0);
|
|
|
|
|
if (mode == VOIDmode)
|
|
|
|
|
mode = GET_MODE (src1);
|
|
|
|
|
|
|
|
|
|
/* 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_relational_operation (GET_CODE (src),
|
|
|
|
|
mode, src0, src1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '1':
|
|
|
|
|
{
|
|
|
|
|
rtx src0 = XEXP (src, 0);
|
|
|
|
|
enum machine_mode mode0 = GET_MODE (src0);
|
|
|
|
|
|
|
|
|
|
/* 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,
|
|
|
|
|
mode0);
|
|
|
|
|
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 ()
|
|
|
|
|
{
|
|
|
|
|
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_insn_after_basic_block_note (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_insn_after_basic_block_note (edge->dest);
|
|
|
|
|
|
|
|
|
|
while (PHI_NODE_P (insn))
|
|
|
|
|
{
|
|
|
|
|
remove_phi_alternative (PATTERN (insn), edge->src);
|
|
|
|
|
if (rtl_dump_file)
|
|
|
|
|
fprintf (rtl_dump_file,
|
|
|
|
|
"Removing alternative for bb %d of phi %d\n",
|
|
|
|
|
edge->src->index, SSA_NAME (PATTERN (insn)));
|
|
|
|
|
insn = NEXT_INSN (insn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rtl_dump_file)
|
|
|
|
|
fprintf (rtl_dump_file,
|
|
|
|
|
"Removing unexecutable edge from %d to %d\n",
|
|
|
|
|
edge->src->index, edge->dest->index);
|
|
|
|
|
/* 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 ()
|
|
|
|
|
{
|
|
|
|
|
unsigned 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.
|
|
|
|
|
|
|
|
|
|
Don't do anything to nodes that were already sets to
|
|
|
|
|
constants. */
|
|
|
|
|
if (! PHI_NODE_P (def)
|
|
|
|
|
&& ! ((GET_CODE (def) == INSN
|
|
|
|
|
&& GET_CODE (SET_SRC (set)) == CONST_INT)))
|
|
|
|
|
{
|
|
|
|
|
if (rtl_dump_file)
|
|
|
|
|
fprintf (rtl_dump_file,
|
|
|
|
|
"Register %d is now set to a constant\n",
|
|
|
|
|
SSA_NAME (PATTERN (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))
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (validate_replace_src (regno_reg_rtx [i],
|
|
|
|
|
values[i].const_value,
|
|
|
|
|
useinsn))
|
|
|
|
|
{
|
|
|
|
|
if (rtl_dump_file)
|
|
|
|
|
fprintf (rtl_dump_file,
|
|
|
|
|
"Register %d in insn %d replaced with constant\n",
|
|
|
|
|
i, INSN_UID (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->flags & BB_REACHABLE))
|
|
|
|
|
{
|
|
|
|
|
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 ()
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
sbitmap_free (ssa_edges);
|
|
|
|
|
ssa_edges = 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)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
VARRAY_RTX (ssa_definition, reg) = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sbitmap_free (worklist);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
|
|
|
|
|
/* Update the use-def chains in the df_analyzer as needed. */
|
|
|
|
|
df_analyse (df_analyzer, 0,
|
|
|
|
|
DF_RD_CHAIN | DF_RU_CHAIN | DF_REG_INFO | DF_HARD_REGS);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|