/* Common subexpression elimination for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GCC.
<http://www.gnu.org/licenses/>. */
#include "config.h"
-/* stdio.h must precede rtl.h for FFS. */
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "regs.h"
#include "basic-block.h"
#include "flags.h"
-#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "function.h"
#include "expr.h"
+#include "diagnostic-core.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
/* Insn being scanned. */
static rtx this_insn;
+static bool optimize_this_for_speed_p;
/* Index by register number, gives the number of the next (or
previous) register in the chain of registers sharing the same
#define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0)
+/* Compare table_elt X and Y and return true iff X is cheaper than Y. */
+
+#define CHEAPER(X, Y) \
+ (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
+
static struct table_elt *table[HASH_SIZE];
/* Chain of `struct table_elt's made so far for this function
static int constant_pool_entries_cost;
static int constant_pool_entries_regcost;
+/* Trace a patch through the CFG. */
+
+struct branch_path
+{
+ /* The basic block for this path entry. */
+ basic_block bb;
+};
+
/* This data describes a block that will be processed by
cse_extended_basic_block. */
/* Size of current branch path, if any. */
int path_size;
/* Current path, indicating which basic_blocks will be processed. */
- struct branch_path
- {
- /* The basic block for this path entry. */
- basic_block bb;
- } *path;
+ struct branch_path *path;
};
static struct table_elt *lookup (rtx, unsigned, enum machine_mode);
static struct table_elt *lookup_for_remove (rtx, unsigned, enum machine_mode);
static rtx lookup_as_function (rtx, enum rtx_code);
+static struct table_elt *insert_with_costs (rtx, struct table_elt *, unsigned,
+ enum machine_mode, int, int);
static struct table_elt *insert (rtx, struct table_elt *, unsigned,
enum machine_mode);
static void merge_equiv_classes (struct table_elt *, struct table_elt *);
static inline unsigned canon_hash (rtx, enum machine_mode);
static inline unsigned safe_hash (rtx, enum machine_mode);
-static unsigned hash_rtx_string (const char *);
+static inline unsigned hash_rtx_string (const char *);
static rtx canon_reg (rtx, rtx);
static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *,
static int cse_change_cc_mode (rtx *, void *);
static void cse_change_cc_mode_insn (rtx, rtx);
static void cse_change_cc_mode_insns (rtx, rtx, rtx);
-static enum machine_mode cse_cc_succs (basic_block, rtx, rtx, bool);
+static enum machine_mode cse_cc_succs (basic_block, basic_block, rtx, rtx,
+ bool);
\f
#undef RTL_HOOKS_GEN_LOWPART
return false;
case PLUS:
- if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ if (!CONST_INT_P (XEXP (x, 1)))
return false;
return fixed_base_plus_p (XEXP (x, 0));
{
if (regno < FIRST_PSEUDO_REGISTER)
{
- if (SMALL_REGISTER_CLASSES)
+ if (targetm.small_register_classes_for_mode_p (GET_MODE (x)))
return 1;
*cost_p += 2;
}
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (x)),
GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))))
? 0
- : rtx_cost (x, outer) * 2);
+ : rtx_cost (x, outer, optimize_this_for_speed_p) * 2);
}
\f
OLD is not changing; NEW is. */
static void
-make_regs_eqv (unsigned int new, unsigned int old)
+make_regs_eqv (unsigned int new_reg, unsigned int old_reg)
{
unsigned int lastr, firstr;
- int q = REG_QTY (old);
+ int q = REG_QTY (old_reg);
struct qty_table_elem *ent;
ent = &qty_table[q];
/* Nothing should become eqv until it has a "non-invalid" qty number. */
- gcc_assert (REGNO_QTY_VALID_P (old));
+ gcc_assert (REGNO_QTY_VALID_P (old_reg));
- REG_QTY (new) = q;
+ REG_QTY (new_reg) = q;
firstr = ent->first_reg;
lastr = ent->last_reg;
that not only can they not be allocated by the compiler, but
they cannot be used in substitutions or canonicalizations
either. */
- && (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS)
- && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new))
- || (new >= FIRST_PSEUDO_REGISTER
+ && (new_reg >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new_reg) != NO_REGS)
+ && ((new_reg < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new_reg))
+ || (new_reg >= FIRST_PSEUDO_REGISTER
&& (firstr < FIRST_PSEUDO_REGISTER
- || (bitmap_bit_p (cse_ebb_live_out, new)
+ || (bitmap_bit_p (cse_ebb_live_out, new_reg)
&& !bitmap_bit_p (cse_ebb_live_out, firstr))
- || (bitmap_bit_p (cse_ebb_live_in, new)
+ || (bitmap_bit_p (cse_ebb_live_in, new_reg)
&& !bitmap_bit_p (cse_ebb_live_in, firstr))))))
{
- reg_eqv_table[firstr].prev = new;
- reg_eqv_table[new].next = firstr;
- reg_eqv_table[new].prev = -1;
- ent->first_reg = new;
+ reg_eqv_table[firstr].prev = new_reg;
+ reg_eqv_table[new_reg].next = firstr;
+ reg_eqv_table[new_reg].prev = -1;
+ ent->first_reg = new_reg;
}
else
{
equivalent for anything. */
while (lastr < FIRST_PSEUDO_REGISTER && reg_eqv_table[lastr].prev >= 0
&& (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr))
- && new >= FIRST_PSEUDO_REGISTER)
+ && new_reg >= FIRST_PSEUDO_REGISTER)
lastr = reg_eqv_table[lastr].prev;
- reg_eqv_table[new].next = reg_eqv_table[lastr].next;
+ reg_eqv_table[new_reg].next = reg_eqv_table[lastr].next;
if (reg_eqv_table[lastr].next >= 0)
- reg_eqv_table[reg_eqv_table[lastr].next].prev = new;
+ reg_eqv_table[reg_eqv_table[lastr].next].prev = new_reg;
else
- qty_table[q].last_reg = new;
- reg_eqv_table[lastr].next = new;
- reg_eqv_table[new].prev = lastr;
+ qty_table[q].last_reg = new_reg;
+ reg_eqv_table[lastr].next = new_reg;
+ reg_eqv_table[new_reg].prev = lastr;
}
}
return mention_regs (x);
}
\f
+
+/* Compute upper and lower anchors for CST. Also compute the offset of CST
+ from these anchors/bases such that *_BASE + *_OFFS = CST. Return false iff
+ CST is equal to an anchor. */
+
+static bool
+compute_const_anchors (rtx cst,
+ HOST_WIDE_INT *lower_base, HOST_WIDE_INT *lower_offs,
+ HOST_WIDE_INT *upper_base, HOST_WIDE_INT *upper_offs)
+{
+ HOST_WIDE_INT n = INTVAL (cst);
+
+ *lower_base = n & ~(targetm.const_anchor - 1);
+ if (*lower_base == n)
+ return false;
+
+ *upper_base =
+ (n + (targetm.const_anchor - 1)) & ~(targetm.const_anchor - 1);
+ *upper_offs = n - *upper_base;
+ *lower_offs = n - *lower_base;
+ return true;
+}
+
+/* Insert the equivalence between ANCHOR and (REG + OFF) in mode MODE. */
+
+static void
+insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs,
+ enum machine_mode mode)
+{
+ struct table_elt *elt;
+ unsigned hash;
+ rtx anchor_exp;
+ rtx exp;
+
+ anchor_exp = GEN_INT (anchor);
+ hash = HASH (anchor_exp, mode);
+ elt = lookup (anchor_exp, hash, mode);
+ if (!elt)
+ elt = insert (anchor_exp, NULL, hash, mode);
+
+ exp = plus_constant (reg, offs);
+ /* REG has just been inserted and the hash codes recomputed. */
+ mention_regs (exp);
+ hash = HASH (exp, mode);
+
+ /* Use the cost of the register rather than the whole expression. When
+ looking up constant anchors we will further offset the corresponding
+ expression therefore it does not make sense to prefer REGs over
+ reg-immediate additions. Prefer instead the oldest expression. Also
+ don't prefer pseudos over hard regs so that we derive constants in
+ argument registers from other argument registers rather than from the
+ original pseudo that was used to synthesize the constant. */
+ insert_with_costs (exp, elt, hash, mode, COST (reg), 1);
+}
+
+/* The constant CST is equivalent to the register REG. Create
+ equivalences between the two anchors of CST and the corresponding
+ register-offset expressions using REG. */
+
+static void
+insert_const_anchors (rtx reg, rtx cst, enum machine_mode mode)
+{
+ HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs;
+
+ if (!compute_const_anchors (cst, &lower_base, &lower_offs,
+ &upper_base, &upper_offs))
+ return;
+
+ /* Ignore anchors of value 0. Constants accessible from zero are
+ simple. */
+ if (lower_base != 0)
+ insert_const_anchor (lower_base, reg, -lower_offs, mode);
+
+ if (upper_base != 0)
+ insert_const_anchor (upper_base, reg, -upper_offs, mode);
+}
+
+/* We need to express ANCHOR_ELT->exp + OFFS. Walk the equivalence list of
+ ANCHOR_ELT and see if offsetting any of the entries by OFFS would create a
+ valid expression. Return the cheapest and oldest of such expressions. In
+ *OLD, return how old the resulting expression is compared to the other
+ equivalent expressions. */
+
+static rtx
+find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs,
+ unsigned *old)
+{
+ struct table_elt *elt;
+ unsigned idx;
+ struct table_elt *match_elt;
+ rtx match;
+
+ /* Find the cheapest and *oldest* expression to maximize the chance of
+ reusing the same pseudo. */
+
+ match_elt = NULL;
+ match = NULL_RTX;
+ for (elt = anchor_elt->first_same_value, idx = 0;
+ elt;
+ elt = elt->next_same_value, idx++)
+ {
+ if (match_elt && CHEAPER (match_elt, elt))
+ return match;
+
+ if (REG_P (elt->exp)
+ || (GET_CODE (elt->exp) == PLUS
+ && REG_P (XEXP (elt->exp, 0))
+ && GET_CODE (XEXP (elt->exp, 1)) == CONST_INT))
+ {
+ rtx x;
+
+ /* Ignore expressions that are no longer valid. */
+ if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false))
+ continue;
+
+ x = plus_constant (elt->exp, offs);
+ if (REG_P (x)
+ || (GET_CODE (x) == PLUS
+ && IN_RANGE (INTVAL (XEXP (x, 1)),
+ -targetm.const_anchor,
+ targetm.const_anchor - 1)))
+ {
+ match = x;
+ match_elt = elt;
+ *old = idx;
+ }
+ }
+ }
+
+ return match;
+}
+
+/* Try to express the constant SRC_CONST using a register+offset expression
+ derived from a constant anchor. Return it if successful or NULL_RTX,
+ otherwise. */
+
+static rtx
+try_const_anchors (rtx src_const, enum machine_mode mode)
+{
+ struct table_elt *lower_elt, *upper_elt;
+ HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs;
+ rtx lower_anchor_rtx, upper_anchor_rtx;
+ rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX;
+ unsigned lower_old, upper_old;
+
+ if (!compute_const_anchors (src_const, &lower_base, &lower_offs,
+ &upper_base, &upper_offs))
+ return NULL_RTX;
+
+ lower_anchor_rtx = GEN_INT (lower_base);
+ upper_anchor_rtx = GEN_INT (upper_base);
+ lower_elt = lookup (lower_anchor_rtx, HASH (lower_anchor_rtx, mode), mode);
+ upper_elt = lookup (upper_anchor_rtx, HASH (upper_anchor_rtx, mode), mode);
+
+ if (lower_elt)
+ lower_exp = find_reg_offset_for_const (lower_elt, lower_offs, &lower_old);
+ if (upper_elt)
+ upper_exp = find_reg_offset_for_const (upper_elt, upper_offs, &upper_old);
+
+ if (!lower_exp)
+ return upper_exp;
+ if (!upper_exp)
+ return lower_exp;
+
+ /* Return the older expression. */
+ return (upper_old > lower_old ? upper_exp : lower_exp);
+}
+\f
/* Look in or update the hash table. */
/* Remove table element ELT from use in the table.
struct table_elt *p
= lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x));
- /* If we are looking for a CONST_INT, the mode doesn't really matter, as
- long as we are narrowing. So if we looked in vain for a mode narrower
- than word_mode before, look for word_mode now. */
- if (p == 0 && code == CONST_INT
- && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode))
- {
- x = copy_rtx (x);
- PUT_MODE (x, word_mode);
- p = lookup (x, SAFE_HASH (x, VOIDmode), word_mode);
- }
-
if (p == 0)
return 0;
return 0;
}
-/* Insert X in the hash table, assuming HASH is its hash code
- and CLASSP is an element of the class it should go in
- (or 0 if a new class should be made).
- It is inserted at the proper position to keep the class in
- the order cheapest first.
+/* Insert X in the hash table, assuming HASH is its hash code and
+ CLASSP is an element of the class it should go in (or 0 if a new
+ class should be made). COST is the code of X and reg_cost is the
+ cost of registers in X. It is inserted at the proper position to
+ keep the class in the order cheapest first.
MODE is the machine-mode of X, or if X is an integer constant
with VOIDmode then MODE is the mode with which X will be used.
If necessary, update table showing constant values of quantities. */
-#define CHEAPER(X, Y) \
- (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
-
static struct table_elt *
-insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode)
+insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash,
+ enum machine_mode mode, int cost, int reg_cost)
{
struct table_elt *elt;
elt->exp = x;
elt->canon_exp = NULL_RTX;
- elt->cost = COST (x);
- elt->regcost = approx_reg_cost (x);
+ elt->cost = cost;
+ elt->regcost = reg_cost;
elt->next_same_value = 0;
elt->prev_same_value = 0;
elt->next_same_hash = table[hash];
return elt;
}
+
+/* Wrap insert_with_costs by passing the default costs. */
+
+static struct table_elt *
+insert (rtx x, struct table_elt *classp, unsigned int hash,
+ enum machine_mode mode)
+{
+ return
+ insert_with_costs (x, classp, hash, mode, COST (x), approx_reg_cost (x));
+}
+
\f
/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from
CLASS2 into CLASS1. This is done when we have reached an insn which makes
static void
merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
{
- struct table_elt *elt, *next, *new;
+ struct table_elt *elt, *next, *new_elt;
/* Ensure we start with the head of the classes. */
class1 = class1->first_same_value;
rehash_using_reg (exp);
hash = HASH (exp, mode);
}
- new = insert (exp, class1, hash, mode);
- new->in_memory = hash_arg_in_memory;
+ new_elt = insert (exp, class1, hash, mode);
+ new_elt->in_memory = hash_arg_in_memory;
}
}
}
{
struct check_dependence_data *d = (struct check_dependence_data *) data;
if (*x && MEM_P (*x))
- return canon_true_dependence (d->exp, d->mode, d->addr, *x,
+ return canon_true_dependence (d->exp, d->mode, d->addr, *x, NULL_RTX,
cse_rtx_varies_p);
else
return 0;
return plus_constant (q->exp, offset);
}
\f
+
/* Hash a string. Just add its bytes up. */
static inline unsigned
hash_rtx_string (const char *ps)
return hash;
}
-/* Hash an rtx. We are careful to make sure the value is never negative.
- Equivalent registers hash identically.
- MODE is used in hashing for CONST_INTs only;
- otherwise the mode of X is used.
-
- Store 1 in DO_NOT_RECORD_P if any subexpression is volatile.
-
- If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains
- a MEM rtx which does not have the RTX_UNCHANGING_P bit set.
-
- Note that cse_insn knows that the hash code of a MEM expression
- is just (int) MEM plus the hash code of the address. */
+/* Same as hash_rtx, but call CB on each rtx if it is not NULL.
+ When the callback returns true, we continue with the new rtx. */
unsigned
-hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p,
- int *hash_arg_in_memory_p, bool have_reg_qty)
+hash_rtx_cb (const_rtx x, enum machine_mode mode,
+ int *do_not_record_p, int *hash_arg_in_memory_p,
+ bool have_reg_qty, hash_rtx_callback_function cb)
{
int i, j;
unsigned hash = 0;
enum rtx_code code;
const char *fmt;
+ enum machine_mode newmode;
+ rtx newx;
/* Used to turn recursion into iteration. We can't rely on GCC's
tail-recursion elimination since we need to keep accumulating values
if (x == 0)
return hash;
+ /* Invoke the callback first. */
+ if (cb != NULL
+ && ((*cb) (x, mode, &newx, &newmode)))
+ {
+ hash += hash_rtx_cb (newx, newmode, do_not_record_p,
+ hash_arg_in_memory_p, have_reg_qty, cb);
+ return hash;
+ }
+
code = GET_CODE (x);
switch (code)
{
{
unsigned int regno = REGNO (x);
- if (!reload_completed)
+ if (do_not_record_p && !reload_completed)
{
/* On some machines, we can't record any non-fixed hard register,
because extending its life will cause reload problems. We
record = true;
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
record = true;
- else if (SMALL_REGISTER_CLASSES)
+ else if (targetm.small_register_classes_for_mode_p (GET_MODE (x)))
record = false;
else if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno)))
record = false;
for (i = 0; i < units; ++i)
{
elt = CONST_VECTOR_ELT (x, i);
- hash += hash_rtx (elt, GET_MODE (elt), do_not_record_p,
- hash_arg_in_memory_p, have_reg_qty);
+ hash += hash_rtx_cb (elt, GET_MODE (elt),
+ do_not_record_p, hash_arg_in_memory_p,
+ have_reg_qty, cb);
}
return hash;
case MEM:
/* We don't record if marked volatile or if BLKmode since we don't
know the size of the move. */
- if (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)
+ if (do_not_record_p && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode))
{
*do_not_record_p = 1;
return 0;
case CC0:
case CALL:
case UNSPEC_VOLATILE:
- *do_not_record_p = 1;
- return 0;
+ if (do_not_record_p) {
+ *do_not_record_p = 1;
+ return 0;
+ }
+ else
+ return hash;
+ break;
case ASM_OPERANDS:
- if (MEM_VOLATILE_P (x))
+ if (do_not_record_p && MEM_VOLATILE_P (x))
{
*do_not_record_p = 1;
return 0;
{
for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
{
- hash += (hash_rtx (ASM_OPERANDS_INPUT (x, i),
- GET_MODE (ASM_OPERANDS_INPUT (x, i)),
- do_not_record_p, hash_arg_in_memory_p,
- have_reg_qty)
+ hash += (hash_rtx_cb (ASM_OPERANDS_INPUT (x, i),
+ GET_MODE (ASM_OPERANDS_INPUT (x, i)),
+ do_not_record_p, hash_arg_in_memory_p,
+ have_reg_qty, cb)
+ hash_rtx_string
- (ASM_OPERANDS_INPUT_CONSTRAINT (x, i)));
+ (ASM_OPERANDS_INPUT_CONSTRAINT (x, i)));
}
hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
goto repeat;
}
- hash += hash_rtx (XEXP (x, i), 0, do_not_record_p,
- hash_arg_in_memory_p, have_reg_qty);
+ hash += hash_rtx_cb (XEXP (x, i), VOIDmode, do_not_record_p,
+ hash_arg_in_memory_p,
+ have_reg_qty, cb);
break;
case 'E':
for (j = 0; j < XVECLEN (x, i); j++)
- hash += hash_rtx (XVECEXP (x, i, j), 0, do_not_record_p,
- hash_arg_in_memory_p, have_reg_qty);
+ hash += hash_rtx_cb (XVECEXP (x, i, j), VOIDmode, do_not_record_p,
+ hash_arg_in_memory_p,
+ have_reg_qty, cb);
break;
case 's':
return hash;
}
+/* Hash an rtx. We are careful to make sure the value is never negative.
+ Equivalent registers hash identically.
+ MODE is used in hashing for CONST_INTs only;
+ otherwise the mode of X is used.
+
+ Store 1 in DO_NOT_RECORD_P if any subexpression is volatile.
+
+ If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains
+ a MEM rtx which does not have the RTX_UNCHANGING_P bit set.
+
+ Note that cse_insn knows that the hash code of a MEM expression
+ is just (int) MEM plus the hash code of the address. */
+
+unsigned
+hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p,
+ int *hash_arg_in_memory_p, bool have_reg_qty)
+{
+ return hash_rtx_cb (x, mode, do_not_record_p,
+ hash_arg_in_memory_p, have_reg_qty, NULL);
+}
+
/* Hash an rtx X for cse via hash_rtx.
Stores 1 in do_not_record if any subexpression is volatile.
Stores 1 in hash_arg_in_memory if X contains a mem rtx which
if (GET_MODE (x) != GET_MODE (y))
return 0;
+ /* MEMs refering to different address space are not equivalent. */
+ if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (y))
+ return 0;
+
switch (code)
{
case PC:
case MEM:
if (for_gcse)
{
+ /* Can't merge two expressions in different alias sets, since we
+ can decide that the expression is transparent in a block when
+ it isn't, due to it being set with the different alias set. */
+ if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
+ return 0;
+
/* A volatile mem should not be considered equivalent to any
other. */
if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;
-
- /* Can't merge two expressions in different alias sets, since we
- can decide that the expression is transparent in a block when
- it isn't, due to it being set with the different alias set.
-
- Also, can't merge two expressions with different MEM_ATTRS.
- They could e.g. be two different entities allocated into the
- same space on the stack (see e.g. PR25130). In that case, the
- MEM addresses can be the same, even though the two MEMs are
- absolutely not equivalent.
-
- But because really all MEM attributes should be the same for
- equivalent MEMs, we just use the invariant that MEMs that have
- the same attributes share the same mem_attrs data structure. */
- if (MEM_ATTRS (x) != MEM_ATTRS (y))
- return 0;
}
break;
}
if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && CONST_INT_P (XEXP (x, 1))
&& REG_P (XEXP (x, 0))
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
{
{
if (*xloc)
{
- rtx new = canon_reg (*xloc, insn);
+ rtx new_rtx = canon_reg (*xloc, insn);
/* If replacing pseudo with hard reg or vice versa, ensure the
insn remains valid. Likewise if the insn has MATCH_DUPs. */
- gcc_assert (insn && new);
- validate_change (insn, xloc, new, 1);
+ gcc_assert (insn && new_rtx);
+ validate_change (insn, xloc, new_rtx, 1);
}
}
enum machine_mode mode;
const char *fmt;
int i;
- rtx new = 0;
+ rtx new_rtx = 0;
int changed = 0;
/* Operands of X. */
{
case MEM:
case SUBREG:
- if ((new = equiv_constant (x)) != NULL_RTX)
- return new;
+ if ((new_rtx = equiv_constant (x)) != NULL_RTX)
+ return new_rtx;
return x;
case CONST:
{
case RTX_UNARY:
{
- int is_const = 0;
-
/* We can't simplify extension ops unless we know the
original mode. */
if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
&& mode_arg0 == VOIDmode)
break;
- /* If we had a CONST, strip it off and put it back later if we
- fold. */
- if (const_arg0 != 0 && GET_CODE (const_arg0) == CONST)
- is_const = 1, const_arg0 = XEXP (const_arg0, 0);
-
- new = simplify_unary_operation (code, mode,
+ new_rtx = simplify_unary_operation (code, mode,
const_arg0 ? const_arg0 : folded_arg0,
mode_arg0);
- /* NEG of PLUS could be converted into MINUS, but that causes
- expressions of the form
- (CONST (MINUS (CONST_INT) (SYMBOL_REF)))
- which many ports mistakenly treat as LEGITIMATE_CONSTANT_P.
- FIXME: those ports should be fixed. */
- if (new != 0 && is_const
- && GET_CODE (new) == PLUS
- && (GET_CODE (XEXP (new, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (new, 0)) == LABEL_REF)
- && GET_CODE (XEXP (new, 1)) == CONST_INT)
- new = gen_rtx_CONST (mode, new);
}
break;
if (const_arg0 == 0 || const_arg1 == 0)
{
struct table_elt *p0, *p1;
- rtx true_rtx = const_true_rtx, false_rtx = const0_rtx;
+ rtx true_rtx, false_rtx;
enum machine_mode mode_arg1;
-#ifdef FLOAT_STORE_FLAG_VALUE
if (SCALAR_FLOAT_MODE_P (mode))
{
+#ifdef FLOAT_STORE_FLAG_VALUE
true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
(FLOAT_STORE_FLAG_VALUE (mode), mode));
+#else
+ true_rtx = NULL_RTX;
+#endif
false_rtx = CONST0_RTX (mode);
}
-#endif
+ else
+ {
+ true_rtx = const_true_rtx;
+ false_rtx = const0_rtx;
+ }
code = find_comparison_args (code, &folded_arg0, &folded_arg1,
&mode_arg0, &mode_arg1);
constant through simplifications. */
p = lookup (folded_arg0, SAFE_HASH (folded_arg0, mode_arg0),
mode_arg0);
-
+
if (p != NULL)
{
cheapest_simplification = x;
const_arg1))
|| (REG_P (folded_arg1)
&& (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty))))
- return (comparison_dominates_p (ent->comparison_code, code)
- ? true_rtx : false_rtx);
+ {
+ if (comparison_dominates_p (ent->comparison_code, code))
+ {
+ if (true_rtx)
+ return true_rtx;
+ else
+ break;
+ }
+ else
+ return false_rtx;
+ }
}
}
}
if (y != 0
&& (inner_const = equiv_constant (XEXP (y, 1))) != 0
- && GET_CODE (inner_const) == CONST_INT
+ && CONST_INT_P (inner_const)
&& INTVAL (inner_const) != 0)
folded_arg0 = gen_rtx_IOR (mode_arg0, XEXP (y, 0), inner_const);
}
{
rtx op0 = const_arg0 ? const_arg0 : folded_arg0;
rtx op1 = const_arg1 ? const_arg1 : folded_arg1;
- new = simplify_relational_operation (code, mode, mode_arg0, op0, op1);
+ new_rtx = simplify_relational_operation (code, mode, mode_arg0, op0, op1);
}
break;
the smallest negative number this would overflow: depending
on the mode, this would either just be the same value (and
hence not save anything) or be incorrect. */
- if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT
+ if (const_arg1 != 0 && CONST_INT_P (const_arg1)
&& INTVAL (const_arg1) < 0
/* This used to test
case MINUS:
/* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2).
If so, produce (PLUS Z C2-C). */
- if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
+ if (const_arg1 != 0 && CONST_INT_P (const_arg1))
{
rtx y = lookup_as_function (XEXP (x, 0), PLUS);
- if (y && GET_CODE (XEXP (y, 1)) == CONST_INT)
+ if (y && CONST_INT_P (XEXP (y, 1)))
return fold_rtx (plus_constant (copy_rtx (y),
-INTVAL (const_arg1)),
NULL_RTX);
if the intermediate operation's result has only one reference. */
if (REG_P (folded_arg0)
- && const_arg1 && GET_CODE (const_arg1) == CONST_INT)
+ && const_arg1 && CONST_INT_P (const_arg1))
{
int is_shift
= (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT);
rtx y, inner_const, new_const;
+ rtx canon_const_arg1 = const_arg1;
enum rtx_code associate_code;
if (is_shift
|| INTVAL (const_arg1) < 0))
{
if (SHIFT_COUNT_TRUNCATED)
- const_arg1 = GEN_INT (INTVAL (const_arg1)
- & (GET_MODE_BITSIZE (mode) - 1));
+ canon_const_arg1 = GEN_INT (INTVAL (const_arg1)
+ & (GET_MODE_BITSIZE (mode)
+ - 1));
else
break;
}
break;
inner_const = equiv_constant (fold_rtx (XEXP (y, 1), 0));
- if (!inner_const || GET_CODE (inner_const) != CONST_INT)
+ if (!inner_const || !CONST_INT_P (inner_const))
break;
/* Don't associate these operations if they are a PLUS with the
associate_code = (is_shift || code == MINUS ? PLUS : code);
new_const = simplify_binary_operation (associate_code, mode,
- const_arg1, inner_const);
+ canon_const_arg1,
+ inner_const);
if (new_const == 0)
break;
of shifts. */
if (is_shift
- && GET_CODE (new_const) == CONST_INT
+ && CONST_INT_P (new_const)
&& INTVAL (new_const) >= GET_MODE_BITSIZE (mode))
{
/* As an exception, we can turn an ASHIFTRT of this
break;
}
- new = simplify_binary_operation (code, mode,
+ new_rtx = simplify_binary_operation (code, mode,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1);
break;
case RTX_TERNARY:
case RTX_BITFIELD_OPS:
- new = simplify_ternary_operation (code, mode, mode_arg0,
+ new_rtx = simplify_ternary_operation (code, mode, mode_arg0,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1,
const_arg2 ? const_arg2 : XEXP (x, 2));
break;
}
- return new ? new : x;
+ return new_rtx ? new_rtx : x;
}
\f
/* Return a constant value currently equivalent to X.
if (GET_CODE (x) == SUBREG)
{
- rtx new;
+ enum machine_mode mode = GET_MODE (x);
+ enum machine_mode imode = GET_MODE (SUBREG_REG (x));
+ rtx new_rtx;
/* See if we previously assigned a constant value to this SUBREG. */
- if ((new = lookup_as_function (x, CONST_INT)) != 0
- || (new = lookup_as_function (x, CONST_DOUBLE)) != 0
- || (new = lookup_as_function (x, CONST_FIXED)) != 0)
- return new;
+ if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0
+ || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0
+ || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0)
+ return new_rtx;
+
+ /* If we didn't and if doing so makes sense, see if we previously
+ assigned a constant value to the enclosing word mode SUBREG. */
+ if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode)
+ && GET_MODE_SIZE (word_mode) < GET_MODE_SIZE (imode))
+ {
+ int byte = SUBREG_BYTE (x) - subreg_lowpart_offset (mode, word_mode);
+ if (byte >= 0 && (byte % UNITS_PER_WORD) == 0)
+ {
+ rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte);
+ new_rtx = lookup_as_function (y, CONST_INT);
+ if (new_rtx)
+ return gen_lowpart (mode, new_rtx);
+ }
+ }
+ /* Otherwise see if we already have a constant for the inner REG. */
if (REG_P (SUBREG_REG (x))
- && (new = equiv_constant (SUBREG_REG (x))) != 0)
- return simplify_subreg (GET_MODE (x), SUBREG_REG (x),
- GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
+ && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0)
+ return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x));
return 0;
}
apply_change_group ();
fold_rtx (x, insn);
}
+ else if (DEBUG_INSN_P (insn))
+ canon_reg (PATTERN (insn), insn);
/* Store the equivalent value in SRC_EQV, if different, or if the DEST
is a STRICT_LOW_PART. The latter condition is necessary because SRC_EQV
{
rtx dest = SET_DEST (sets[i].rtl);
rtx src = SET_SRC (sets[i].rtl);
- rtx new = canon_reg (src, insn);
+ rtx new_rtx = canon_reg (src, insn);
- validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
+ validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1);
if (GET_CODE (dest) == ZERO_EXTRACT)
{
for (i = 0; i < n_sets; i++)
{
+ bool repeat = false;
rtx src, dest;
rtx src_folded;
struct table_elt *elt = 0, *p;
rtx src_eqv_here;
rtx src_const = 0;
rtx src_related = 0;
+ bool src_related_is_const_anchor = false;
struct table_elt *src_const_elt = 0;
int src_cost = MAX_COST;
int src_eqv_cost = MAX_COST;
{
rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
- if (GET_CODE (src) == CONST_INT
- && GET_CODE (width) == CONST_INT
+ if (CONST_INT_P (src)
+ && CONST_INT_P (width)
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
src_folded
/* See if we have a CONST_INT that is already in a register in a
wider mode. */
- if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT
+ if (src_const && src_related == 0 && CONST_INT_P (src_const)
&& GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_BITSIZE (mode) < BITS_PER_WORD)
{
enum machine_mode wider_mode;
for (wider_mode = GET_MODE_WIDER_MODE (mode);
- GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
+ wider_mode != VOIDmode
+ && GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
&& src_related == 0;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
value. */
if (flag_expensive_optimizations && ! src_related
- && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT
+ && GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1))
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
{
enum machine_mode tmode;
}
#endif /* LOAD_EXTEND_OP */
+ /* Try to express the constant using a register+offset expression
+ derived from a constant anchor. */
+
+ if (targetm.const_anchor
+ && !src_related
+ && src_const
+ && GET_CODE (src_const) == CONST_INT)
+ {
+ src_related = try_const_anchors (src_const, mode);
+ src_related_is_const_anchor = src_related != NULL_RTX;
+ }
+
+
if (src == src_folded)
src_folded = 0;
{
src_related_cost = COST (src_related);
src_related_regcost = approx_reg_cost (src_related);
+
+ /* If a const-anchor is used to synthesize a constant that
+ normally requires multiple instructions then slightly prefer
+ it over the original sequence. These instructions are likely
+ to become redundant now. We can't compare against the cost
+ of src_eqv_here because, on MIPS for example, multi-insn
+ constants have zero cost; they are assumed to be hoisted from
+ loops. */
+ if (src_related_is_const_anchor
+ && src_related_cost == src_cost
+ && src_eqv_here)
+ src_related_cost--;
}
}
break;
}
+ /* Try to optimize
+ (set (reg:M N) (const_int A))
+ (set (reg:M2 O) (const_int B))
+ (set (zero_extract:M2 (reg:M N) (const_int C) (const_int D))
+ (reg:M2 O)). */
+ if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT
+ && CONST_INT_P (trial)
+ && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 1))
+ && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 2))
+ && REG_P (XEXP (SET_DEST (sets[i].rtl), 0))
+ && (GET_MODE_BITSIZE (GET_MODE (SET_DEST (sets[i].rtl)))
+ >= INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)))
+ && ((unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 1))
+ + (unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 2))
+ <= HOST_BITS_PER_WIDE_INT))
+ {
+ rtx dest_reg = XEXP (SET_DEST (sets[i].rtl), 0);
+ rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
+ rtx pos = XEXP (SET_DEST (sets[i].rtl), 2);
+ unsigned int dest_hash = HASH (dest_reg, GET_MODE (dest_reg));
+ struct table_elt *dest_elt
+ = lookup (dest_reg, dest_hash, GET_MODE (dest_reg));
+ rtx dest_cst = NULL;
+
+ if (dest_elt)
+ for (p = dest_elt->first_same_value; p; p = p->next_same_value)
+ if (p->is_const && CONST_INT_P (p->exp))
+ {
+ dest_cst = p->exp;
+ break;
+ }
+ if (dest_cst)
+ {
+ HOST_WIDE_INT val = INTVAL (dest_cst);
+ HOST_WIDE_INT mask;
+ unsigned int shift;
+ if (BITS_BIG_ENDIAN)
+ shift = GET_MODE_BITSIZE (GET_MODE (dest_reg))
+ - INTVAL (pos) - INTVAL (width);
+ else
+ shift = INTVAL (pos);
+ if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
+ mask = ~(HOST_WIDE_INT) 0;
+ else
+ mask = ((HOST_WIDE_INT) 1 << INTVAL (width)) - 1;
+ val &= ~(mask << shift);
+ val |= (INTVAL (trial) & mask) << shift;
+ val = trunc_int_for_mode (val, GET_MODE (dest_reg));
+ validate_unshare_change (insn, &SET_DEST (sets[i].rtl),
+ dest_reg, 1);
+ validate_unshare_change (insn, &SET_SRC (sets[i].rtl),
+ GEN_INT (val), 1);
+ if (apply_change_group ())
+ {
+ rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+ if (note)
+ {
+ remove_note (insn, note);
+ df_notes_rescan (insn);
+ }
+ src_eqv = NULL_RTX;
+ src_eqv_elt = NULL;
+ src_eqv_volatile = 0;
+ src_eqv_in_memory = 0;
+ src_eqv_hash = 0;
+ repeat = true;
+ break;
+ }
+ }
+ }
+
/* We don't normally have an insn matching (set (pc) (pc)), so
check for this separately here. We will delete such an
insn below.
else if (validate_unshare_change
(insn, &SET_SRC (sets[i].rtl), trial, 0))
{
- rtx new = canon_reg (SET_SRC (sets[i].rtl), insn);
+ rtx new_rtx = canon_reg (SET_SRC (sets[i].rtl), insn);
/* The result of apply_change_group can be ignored; see
canon_reg. */
- validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
+ validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1);
apply_change_group ();
break;
}
}
+ /* If we changed the insn too much, handle this set from scratch. */
+ if (repeat)
+ {
+ i--;
+ continue;
+ }
+
src = SET_SRC (sets[i].rtl);
/* In general, it is good to have a SET with SET_SRC == SET_DEST.
{
rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
- if (src_const != 0 && GET_CODE (src_const) == CONST_INT
- && GET_CODE (width) == CONST_INT
+ if (src_const != 0 && CONST_INT_P (src_const)
+ && CONST_INT_P (width)
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& ! (INTVAL (src_const)
& ((HOST_WIDE_INT) (-1) << INTVAL (width))))
and hope for the best. */
if (n_sets == 1)
{
- rtx new, note;
+ rtx new_rtx, note;
- new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
- JUMP_LABEL (new) = XEXP (src, 0);
+ new_rtx = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+ JUMP_LABEL (new_rtx) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
/* Make sure to copy over REG_NON_LOCAL_GOTO. */
if (note)
{
XEXP (note, 1) = NULL_RTX;
- REG_NOTES (new) = note;
+ REG_NOTES (new_rtx) = note;
}
delete_insn_and_edges (insn);
- insn = new;
+ insn = new_rtx;
}
else
INSN_CODE (insn) = -1;
elt = insert (dest, sets[i].src_elt,
sets[i].dest_hash, GET_MODE (dest));
+ /* If this is a constant, insert the constant anchors with the
+ equivalent register-offset expressions using register DEST. */
+ if (targetm.const_anchor
+ && REG_P (dest)
+ && SCALAR_INT_MODE_P (GET_MODE (dest))
+ && GET_CODE (sets[i].src_elt->exp) == CONST_INT)
+ insert_const_anchors (dest, sets[i].src_elt->exp, GET_MODE (dest));
+
elt->in_memory = (MEM_P (sets[i].inner_dest)
&& !MEM_READONLY_P (sets[i].inner_dest));
{
prev = PREV_INSN (prev);
}
- while (prev != bb_head && NOTE_P (prev));
+ while (prev != bb_head && (NOTE_P (prev) || DEBUG_INSN_P (prev)));
/* Do not swap the registers around if the previous instruction
attaches a REG_EQUIV note to REG1.
case ZERO_EXTEND:
case SUBREG:
{
- rtx new = cse_process_notes (XEXP (x, 0), object, changed);
+ rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed);
/* We don't substitute VOIDmode constants into these rtx,
since they would impede folding. */
- if (GET_MODE (new) != VOIDmode)
- validate_change (object, &XEXP (x, 0), new, 0);
+ if (GET_MODE (new_rtx) != VOIDmode)
+ validate_change (object, &XEXP (x, 0), new_rtx, 0);
return x;
}
&& (CONSTANT_P (ent->const_rtx)
|| REG_P (ent->const_rtx)))
{
- rtx new = gen_lowpart (GET_MODE (x), ent->const_rtx);
- if (new)
- return copy_rtx (new);
+ rtx new_rtx = gen_lowpart (GET_MODE (x), ent->const_rtx);
+ if (new_rtx)
+ return copy_rtx (new_rtx);
}
}
static rtx
cse_process_notes (rtx x, rtx object, bool *changed)
{
- rtx new = cse_process_notes_1 (x, object, changed);
- if (new != x)
+ rtx new_rtx = cse_process_notes_1 (x, object, changed);
+ if (new_rtx != x)
*changed = true;
- return new;
+ return new_rtx;
}
\f
describe the path.
It is filled with a queue of basic blocks, starting with FIRST_BB
and following a trace through the CFG.
-
+
If all paths starting at FIRST_BB have been followed, or no new path
starting at FIRST_BB can be constructed, this function returns FALSE.
Otherwise, DATA->path is filled and the function returns TRUE indicating
basic_block bb;
edge e;
int path_size;
-
+
SET_BIT (cse_visited_basic_blocks, first_bb->index);
/* See if there is a previous path. */
int path_entry;
/* Scan to end of each basic block in the path. */
- for (path_entry = 0; path_entry < path_size; path_entry++)
+ for (path_entry = 0; path_entry < path_size; path_entry++)
{
basic_block bb;
rtx insn;
edge pointing to that bb. */
if (bb_has_eh_pred (bb))
{
- struct df_ref **def_rec;
+ df_ref *def_rec;
for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
{
- struct df_ref *def = *def_rec;
+ df_ref def = *def_rec;
if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
invalidate (DF_REF_REG (def), GET_MODE (DF_REF_REG (def)));
}
}
+ optimize_this_for_speed_p = optimize_bb_for_speed_p (bb);
FOR_BB_INSNS (bb, insn)
{
/* If we have processed 1,000 insns, flush the hash table to
FIXME: This is a real kludge and needs to be done some other
way. */
- if (INSN_P (insn)
+ if (NONDEBUG_INSN_P (insn)
&& num_insns++ > PARAM_VALUE (PARAM_MAX_CSE_INSNS))
{
flush_hash_table ();
/* With non-call exceptions, we are not always able to update
the CFG properly inside cse_insn. So clean up possibly
redundant EH edges here. */
- if (flag_non_call_exceptions && have_eh_succ_edges (bb))
+ if (cfun->can_throw_non_call_exceptions && have_eh_succ_edges (bb))
cse_cfg_altered |= purge_dead_edges (bb);
/* If we changed a conditional jump, we may have terminated
incr);
return;
+ case DEBUG_INSN:
+ return;
+
case CALL_INSN:
case INSN:
case JUMP_INSN:
- /* We expect dest to be NULL_RTX here. If the insn may trap, mark
- this fact by setting DEST to pc_rtx. */
- if (flag_non_call_exceptions && may_trap_p (PATTERN (x)))
+ /* We expect dest to be NULL_RTX here. If the insn may trap, mark
+ this fact by setting DEST to pc_rtx. */
+ if (insn_could_throw_p (x))
dest = pc_rtx;
if (code == CALL_INSN)
count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr);
}
}
\f
+/* Return true if a register is dead. Can be used in for_each_rtx. */
+
+static int
+is_dead_reg (rtx *loc, void *data)
+{
+ rtx x = *loc;
+ int *counts = (int *)data;
+
+ return (REG_P (x)
+ && REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && counts[REGNO (x)] == 0);
+}
+
/* Return true if set is live. */
static bool
set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0. */
|| !reg_referenced_p (cc0_rtx, PATTERN (tem))))
return false;
#endif
- else if (!REG_P (SET_DEST (set))
- || REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
- || counts[REGNO (SET_DEST (set))] != 0
+ else if (!is_dead_reg (&SET_DEST (set), counts)
|| side_effects_p (SET_SRC (set)))
return true;
return false;
insn_live_p (rtx insn, int *counts)
{
int i;
- if (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))
+ if (insn_could_throw_p (insn))
return true;
else if (GET_CODE (PATTERN (insn)) == SET)
return set_live_p (PATTERN (insn), insn, counts);
}
return false;
}
+ else if (DEBUG_INSN_P (insn))
+ {
+ rtx next;
+
+ for (next = NEXT_INSN (insn); next; next = NEXT_INSN (next))
+ if (NOTE_P (next))
+ continue;
+ else if (!DEBUG_INSN_P (next))
+ return true;
+ else if (INSN_VAR_LOCATION_DECL (insn) == INSN_VAR_LOCATION_DECL (next))
+ return false;
+
+ /* If this debug insn references a dead register, drop the
+ location expression for now. ??? We could try to find the
+ def and see if propagation is possible. */
+ if (for_each_rtx (&INSN_VAR_LOCATION_LOC (insn), is_dead_reg, counts))
+ {
+ INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+ df_insn_rescan (insn);
+ }
+
+ return true;
+ }
else
return true;
}
&& GET_MODE (*loc) != GET_MODE (args->newreg))
{
validate_change (args->insn, loc, args->newreg, 1);
-
+
return -1;
}
return 0;
args.insn = insn;
args.newreg = newreg;
-
+
for_each_rtx (&PATTERN (insn), cse_change_cc_mode, &args);
for_each_rtx (®_NOTES (insn), cse_change_cc_mode, &args);
-
+
/* If the following assertion was triggered, there is most probably
something wrong with the cc_modes_compatible back end function.
CC modes only can be considered compatible if the insn - with the mode
permitted to change the mode of CC_SRC to a compatible mode. This
returns VOIDmode if no equivalent assignments were found.
Otherwise it returns the mode which CC_SRC should wind up with.
+ ORIG_BB should be the same as BB in the outermost cse_cc_succs call,
+ but is passed unmodified down to recursive calls in order to prevent
+ endless recursion.
The main complexity in this function is handling the mode issues.
We may have more than one duplicate which we can eliminate, and we
try to find a mode which will work for multiple duplicates. */
static enum machine_mode
-cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode)
+cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src,
+ bool can_change_mode)
{
bool found_equiv;
enum machine_mode mode;
continue;
if (EDGE_COUNT (e->dest->preds) != 1
- || e->dest == EXIT_BLOCK_PTR)
+ || e->dest == EXIT_BLOCK_PTR
+ /* Avoid endless recursion on unreachable blocks. */
+ || e->dest == orig_bb)
continue;
end = NEXT_INSN (BB_END (e->dest));
XEXP (SET_SRC (set), 0))
&& rtx_equal_p (XEXP (cc_src, 1),
XEXP (SET_SRC (set), 1)))
-
+
{
comp_mode = targetm.cc_modes_compatible (mode, set_mode);
if (comp_mode != VOIDmode
{
enum machine_mode submode;
- submode = cse_cc_succs (e->dest, cc_reg, cc_src, false);
+ submode = cse_cc_succs (e->dest, orig_bb, cc_reg, cc_src, false);
if (submode != VOIDmode)
{
gcc_assert (submode == mode);
the basic block. */
orig_mode = GET_MODE (cc_src);
- mode = cse_cc_succs (bb, cc_reg, cc_src, true);
+ mode = cse_cc_succs (bb, bb, cc_reg, cc_src, true);
if (mode != VOIDmode)
{
gcc_assert (mode == GET_MODE (cc_src));
{
RTL_PASS,
"cse1", /* name */
- gate_handle_cse, /* gate */
- rest_of_handle_cse, /* execute */
+ gate_handle_cse, /* gate */
+ rest_of_handle_cse, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
{
RTL_PASS,
"cse2", /* name */
- gate_handle_cse2, /* gate */
- rest_of_handle_cse2, /* execute */
+ gate_handle_cse2, /* gate */
+ rest_of_handle_cse2, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
}
};
+static bool
+gate_handle_cse_after_global_opts (void)
+{
+ return optimize > 0 && flag_rerun_cse_after_global_opts;
+}
+
+/* Run second CSE pass after loop optimizations. */
+static unsigned int
+rest_of_handle_cse_after_global_opts (void)
+{
+ int save_cfj;
+ int tem;
+
+ /* We only want to do local CSE, so don't follow jumps. */
+ save_cfj = flag_cse_follow_jumps;
+ flag_cse_follow_jumps = 0;
+
+ rebuild_jump_labels (get_insns ());
+ tem = cse_main (get_insns (), max_reg_num ());
+ purge_all_dead_edges ();
+ delete_trivially_dead_insns (get_insns (), max_reg_num ());
+
+ cse_not_expected = !flag_rerun_cse_after_loop;
+
+ /* If cse altered any jumps, rerun jump opts to clean things up. */
+ if (tem == 2)
+ {
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (get_insns ());
+ cleanup_cfg (0);
+ timevar_pop (TV_JUMP);
+ }
+ else if (tem == 1)
+ cleanup_cfg (0);
+
+ flag_cse_follow_jumps = save_cfj;
+ return 0;
+}
+
+struct rtl_opt_pass pass_cse_after_global_opts =
+{
+ {
+ RTL_PASS,
+ "cse_local", /* name */
+ gate_handle_cse_after_global_opts, /* gate */
+ rest_of_handle_cse_after_global_opts, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_CSE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_dump_func |
+ TODO_ggc_collect |
+ TODO_verify_flow /* todo_flags_finish */
+ }
+};