/* Common subexpression elimination library for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
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
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
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. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "cselib.h"
#include "params.h"
#include "alloc-pool.h"
+#include "target.h"
static bool cselib_record_memory;
static int entry_and_rtx_equal_p (const void *, const void *);
static void unchain_one_value (cselib_val *);
static void unchain_one_elt_list (struct elt_list **);
static void unchain_one_elt_loc_list (struct elt_loc_list **);
-static void clear_table (void);
static int discard_useless_locs (void **, void *);
static int discard_useless_values (void **, void *);
static void remove_useless_values (void);
static rtx wrap_constant (enum machine_mode, rtx);
-static unsigned int cselib_hash_rtx (rtx, enum machine_mode, int);
+static unsigned int cselib_hash_rtx (rtx, int);
static cselib_val *new_cselib_val (unsigned int, enum machine_mode);
static void add_mem_for_addr (cselib_val *, cselib_val *, rtx);
static cselib_val *cselib_lookup_mem (rtx, int);
the locations of the entries with the rtx we are looking up. */
/* A table that enables us to look up elts by their value. */
-static htab_t hash_table;
+static htab_t cselib_hash_table;
/* This is a global so we don't have to pass this through every function.
It is used in new_elt_loc_list to set SETTING_INSN. */
static rtx cselib_current_insn;
-static bool cselib_current_insn_in_libcall;
/* Every new unknown value gets a unique number. */
static unsigned int next_unknown_value;
which the register was set; if the mode is unknown or the value is
no longer valid in that mode, ELT will be NULL for the first
element. */
-struct elt_list **reg_values;
-unsigned int reg_values_size;
+static struct elt_list **reg_values;
+static unsigned int reg_values_size;
#define REG_VALUES(i) reg_values[i]
/* The largest number of hard regs used by any entry added to the
- REG_VALUES table. Cleared on each clear_table() invocation. */
+ REG_VALUES table. Cleared on each cselib_clear_table() invocation. */
static unsigned int max_value_regs;
/* Here the set of indices I with REG_VALUES(I) != 0 is saved. This is used
- in clear_table() for fast emptying. */
+ in cselib_clear_table() for fast emptying. */
static unsigned int *used_regs;
static unsigned int n_used_regs;
each time memory is invalidated. */
static cselib_val *first_containing_mem = &dummy_val;
static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool;
+
+/* If nonnull, cselib will call this function before freeing useless
+ VALUEs. A VALUE is deemed useless if its "locs" field is null. */
+void (*cselib_discard_hook) (cselib_val *);
\f
/* Allocate a struct elt_list and fill in its two elements with the
new_elt_list (struct elt_list *next, cselib_val *elt)
{
struct elt_list *el;
- el = pool_alloc (elt_list_pool);
+ el = (struct elt_list *) pool_alloc (elt_list_pool);
el->next = next;
el->elt = elt;
return el;
new_elt_loc_list (struct elt_loc_list *next, rtx loc)
{
struct elt_loc_list *el;
- el = pool_alloc (elt_loc_list_pool);
+ el = (struct elt_loc_list *) pool_alloc (elt_loc_list_pool);
el->next = next;
el->loc = loc;
el->setting_insn = cselib_current_insn;
- el->in_libcall = cselib_current_insn_in_libcall;
return el;
}
initialization. If CLEAR_ALL isn't set, then only clear the entries
which are known to have been used. */
-static void
-clear_table (void)
+void
+cselib_clear_table (void)
{
unsigned int i;
n_used_regs = 0;
- htab_empty (hash_table);
+ htab_empty (cselib_hash_table);
n_useless_values = 0;
entry_and_rtx_equal_p (const void *entry, const void *x_arg)
{
struct elt_loc_list *l;
- const cselib_val *v = (const cselib_val *) entry;
- rtx x = (rtx) x_arg;
+ const cselib_val *const v = (const cselib_val *) entry;
+ rtx x = CONST_CAST_RTX ((const_rtx)x_arg);
enum machine_mode mode = GET_MODE (x);
- gcc_assert (GET_CODE (x) != CONST_INT
+ gcc_assert (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_FIXED
&& (mode != VOIDmode || GET_CODE (x) != CONST_DOUBLE));
- if (mode != GET_MODE (v->u.val_rtx))
+ if (mode != GET_MODE (v->val_rtx))
return 0;
/* Unwrap X if necessary. */
if (GET_CODE (x) == CONST
&& (GET_CODE (XEXP (x, 0)) == CONST_INT
+ || GET_CODE (XEXP (x, 0)) == CONST_FIXED
|| GET_CODE (XEXP (x, 0)) == CONST_DOUBLE))
x = XEXP (x, 0);
static hashval_t
get_value_hash (const void *entry)
{
- const cselib_val *v = (const cselib_val *) entry;
+ const cselib_val *const v = (const cselib_val *) entry;
return v->value;
}
removed. */
int
-references_value_p (rtx x, int only_useless)
+references_value_p (const_rtx x, int only_useless)
{
- enum rtx_code code = GET_CODE (x);
+ const enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
int i, j;
if (v->locs == 0)
{
- CSELIB_VAL_PTR (v->u.val_rtx) = NULL;
- htab_clear_slot (hash_table, x);
+ if (cselib_discard_hook)
+ cselib_discard_hook (v);
+
+ CSELIB_VAL_PTR (v->val_rtx) = NULL;
+ htab_clear_slot (cselib_hash_table, x);
unchain_one_value (v);
n_useless_values--;
}
do
{
values_became_useless = 0;
- htab_traverse (hash_table, discard_useless_locs, 0);
+ htab_traverse (cselib_hash_table, discard_useless_locs, 0);
}
while (values_became_useless);
}
*p = &dummy_val;
- htab_traverse (hash_table, discard_useless_values, 0);
+ htab_traverse (cselib_hash_table, discard_useless_values, 0);
gcc_assert (!n_useless_values);
}
VOIDmode. */
enum machine_mode
-cselib_reg_set_mode (rtx x)
+cselib_reg_set_mode (const_rtx x)
{
if (!REG_P (x))
return GET_MODE (x);
|| REG_VALUES (REGNO (x))->elt == NULL)
return VOIDmode;
- return GET_MODE (REG_VALUES (REGNO (x))->elt->u.val_rtx);
+ return GET_MODE (REG_VALUES (REGNO (x))->elt->val_rtx);
}
/* Return nonzero if we can prove that X and Y contain the same value, taking
cselib_val *e = cselib_lookup (x, GET_MODE (x), 0);
if (e)
- x = e->u.val_rtx;
+ x = e->val_rtx;
}
if (REG_P (y) || MEM_P (y))
cselib_val *e = cselib_lookup (y, GET_MODE (y), 0);
if (e)
- y = e->u.val_rtx;
+ y = e->val_rtx;
}
if (x == y)
if (GET_CODE (x) != GET_CODE (y) || GET_MODE (x) != GET_MODE (y))
return 0;
- /* This won't be handled correctly by the code below. */
- if (GET_CODE (x) == LABEL_REF)
- return XEXP (x, 0) == XEXP (y, 0);
+ /* These won't be handled correctly by the code below. */
+ switch (GET_CODE (x))
+ {
+ case CONST_DOUBLE:
+ case CONST_FIXED:
+ return 0;
+
+ case LABEL_REF:
+ return XEXP (x, 0) == XEXP (y, 0);
+
+ default:
+ break;
+ }
code = GET_CODE (x);
fmt = GET_RTX_FORMAT (code);
break;
case 'e':
+ if (i == 1
+ && targetm.commutative_p (x, UNKNOWN)
+ && rtx_equal_for_cselib_p (XEXP (x, 1), XEXP (y, 0))
+ && rtx_equal_for_cselib_p (XEXP (x, 0), XEXP (y, 1)))
+ return 1;
if (! rtx_equal_for_cselib_p (XEXP (x, i), XEXP (y, i)))
return 0;
break;
static rtx
wrap_constant (enum machine_mode mode, rtx x)
{
- if (GET_CODE (x) != CONST_INT
+ if (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_FIXED
&& (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode))
return x;
gcc_assert (mode != VOIDmode);
Possible reasons for return 0 are: the object is volatile, or we couldn't
find a register or memory location in the table and CREATE is zero. If
CREATE is nonzero, table elts are created for regs and mem.
- MODE is used in hashing for CONST_INTs only;
- otherwise the mode of X is used. */
+ N.B. this hash function returns the same hash value for RTXes that
+ differ only in the order of operands, thus it is suitable for comparisons
+ that take commutativity into account.
+ If we wanted to also support associative rules, we'd have to use a different
+ strategy to avoid returning spurious 0, e.g. return ~(~0U >> 1) .
+ We used to have a MODE argument for hashing for CONST_INTs, but that
+ didn't make sense, since it caused spurious hash differences between
+ (set (reg:SI 1) (const_int))
+ (plus:SI (reg:SI 2) (reg:SI 1))
+ and
+ (plus:SI (reg:SI 2) (const_int))
+ If the mode is important in any context, it must be checked specifically
+ in a comparison anyway, since relying on hash differences is unsafe. */
static unsigned int
-cselib_hash_rtx (rtx x, enum machine_mode mode, int create)
+cselib_hash_rtx (rtx x, int create)
{
cselib_val *e;
int i, j;
return e->value;
case CONST_INT:
- hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + INTVAL (x);
+ hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
return hash ? hash : (unsigned int) CONST_INT;
case CONST_DOUBLE:
+ (unsigned) CONST_DOUBLE_HIGH (x));
return hash ? hash : (unsigned int) CONST_DOUBLE;
+ case CONST_FIXED:
+ hash += (unsigned int) code + (unsigned int) GET_MODE (x);
+ hash += fixed_hash (CONST_FIXED_VALUE (x));
+ return hash ? hash : (unsigned int) CONST_FIXED;
+
case CONST_VECTOR:
{
int units;
for (i = 0; i < units; ++i)
{
elt = CONST_VECTOR_ELT (x, i);
- hash += cselib_hash_rtx (elt, GET_MODE (elt), 0);
+ hash += cselib_hash_rtx (elt, 0);
}
return hash;
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
- hash
- += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
+ /* We don't hash on the address of the CODE_LABEL to avoid bootstrap
+ differences and differences between each stage's debugging dumps. */
+ hash += (((unsigned int) LABEL_REF << 7)
+ + CODE_LABEL_NUMBER (XEXP (x, 0)));
return hash ? hash : (unsigned int) LABEL_REF;
case SYMBOL_REF:
- hash
- += ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0);
- return hash ? hash : (unsigned int) SYMBOL_REF;
+ {
+ /* Don't hash on the symbol's address to avoid bootstrap differences.
+ Different hash values may cause expressions to be recorded in
+ different orders and thus different registers to be used in the
+ final assembler. This also avoids differences in the dump files
+ between various stages. */
+ unsigned int h = 0;
+ const unsigned char *p = (const unsigned char *) XSTR (x, 0);
+
+ while (*p)
+ h += (h << 7) + *p++; /* ??? revisit */
+
+ hash += ((unsigned int) SYMBOL_REF << 7) + h;
+ return hash ? hash : (unsigned int) SYMBOL_REF;
+ }
case PRE_DEC:
case PRE_INC:
case 'e':
{
rtx tem = XEXP (x, i);
- unsigned int tem_hash = cselib_hash_rtx (tem, 0, create);
+ unsigned int tem_hash = cselib_hash_rtx (tem, create);
if (tem_hash == 0)
return 0;
for (j = 0; j < XVECLEN (x, i); j++)
{
unsigned int tem_hash
- = cselib_hash_rtx (XVECEXP (x, i, j), 0, create);
+ = cselib_hash_rtx (XVECEXP (x, i, j), create);
if (tem_hash == 0)
return 0;
static inline cselib_val *
new_cselib_val (unsigned int value, enum machine_mode mode)
{
- cselib_val *e = pool_alloc (cselib_val_pool);
+ cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool);
gcc_assert (value);
e->value = value;
- /* We use custom method to allocate this RTL construct because it accounts
- about 8% of overall memory usage. */
- e->u.val_rtx = pool_alloc (value_pool);
- memset (e->u.val_rtx, 0, RTX_HDR_SIZE);
- PUT_CODE (e->u.val_rtx, VALUE);
- PUT_MODE (e->u.val_rtx, mode);
- CSELIB_VAL_PTR (e->u.val_rtx) = e;
+ /* We use an alloc pool to allocate this RTL construct because it
+ accounts for about 8% of the overall memory usage. We know
+ precisely when we can have VALUE RTXen (when cselib is active)
+ so we don't need to put them in garbage collected memory.
+ ??? Why should a VALUE be an RTX in the first place? */
+ e->val_rtx = (rtx) pool_alloc (value_pool);
+ memset (e->val_rtx, 0, RTX_HDR_SIZE);
+ PUT_CODE (e->val_rtx, VALUE);
+ PUT_MODE (e->val_rtx, mode);
+ CSELIB_VAL_PTR (e->val_rtx) = e;
e->addr_list = 0;
e->locs = 0;
e->next_containing_mem = 0;
addr_elt->addr_list = new_elt_list (addr_elt->addr_list, mem_elt);
mem_elt->locs
= new_elt_loc_list (mem_elt->locs,
- replace_equiv_address_nv (x, addr_elt->u.val_rtx));
+ replace_equiv_address_nv (x, addr_elt->val_rtx));
if (mem_elt->next_containing_mem == NULL)
{
mem_elt->next_containing_mem = first_containing_mem;
/* Find a value that describes a value of our mode at that address. */
for (l = addr->addr_list; l; l = l->next)
- if (GET_MODE (l->elt->u.val_rtx) == mode)
+ if (GET_MODE (l->elt->val_rtx) == mode)
return l->elt;
if (! create)
mem_elt = new_cselib_val (++next_unknown_value, mode);
add_mem_for_addr (addr, mem_elt, x);
- slot = htab_find_slot_with_hash (hash_table, wrap_constant (mode, x),
+ slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
mem_elt->value, INSERT);
*slot = mem_elt;
return mem_elt;
}
+/* Search thru the possible substitutions in P. We prefer a non reg
+ substitution because this allows us to expand the tree further. If
+ we find, just a reg, take the lowest regno. There may be several
+ non-reg results, we just take the first one because they will all
+ expand to the same place. */
+
+static rtx
+expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth)
+{
+ rtx reg_result = NULL;
+ unsigned int regno = UINT_MAX;
+ struct elt_loc_list *p_in = p;
+
+ for (; p; p = p -> next)
+ {
+ /* Avoid infinite recursion trying to expand a reg into a
+ the same reg. */
+ if ((REG_P (p->loc))
+ && (REGNO (p->loc) < regno)
+ && !bitmap_bit_p (regs_active, REGNO (p->loc)))
+ {
+ reg_result = p->loc;
+ regno = REGNO (p->loc);
+ }
+ /* Avoid infinite recursion and do not try to expand the
+ value. */
+ else if (GET_CODE (p->loc) == VALUE
+ && CSELIB_VAL_PTR (p->loc)->locs == p_in)
+ continue;
+ else if (!REG_P (p->loc))
+ {
+ rtx result, note;
+ if (dump_file)
+ {
+ print_inline_rtx (dump_file, p->loc, 0);
+ fprintf (dump_file, "\n");
+ }
+ if (GET_CODE (p->loc) == LO_SUM
+ && GET_CODE (XEXP (p->loc, 1)) == SYMBOL_REF
+ && p->setting_insn
+ && (note = find_reg_note (p->setting_insn, REG_EQUAL, NULL_RTX))
+ && XEXP (note, 0) == XEXP (p->loc, 1))
+ return XEXP (p->loc, 1);
+ result = cselib_expand_value_rtx (p->loc, regs_active, max_depth - 1);
+ if (result)
+ return result;
+ }
+
+ }
+
+ if (regno != UINT_MAX)
+ {
+ rtx result;
+ if (dump_file)
+ fprintf (dump_file, "r%d\n", regno);
+
+ result = cselib_expand_value_rtx (reg_result, regs_active, max_depth - 1);
+ if (result)
+ return result;
+ }
+
+ if (dump_file)
+ {
+ if (reg_result)
+ {
+ print_inline_rtx (dump_file, reg_result, 0);
+ fprintf (dump_file, "\n");
+ }
+ else
+ fprintf (dump_file, "NULL\n");
+ }
+ return reg_result;
+}
+
+
+/* Forward substitute and expand an expression out to its roots.
+ This is the opposite of common subexpression. Because local value
+ numbering is such a weak optimization, the expanded expression is
+ pretty much unique (not from a pointer equals point of view but
+ from a tree shape point of view.
+
+ This function returns NULL if the expansion fails. The expansion
+ will fail if there is no value number for one of the operands or if
+ one of the operands has been overwritten between the current insn
+ and the beginning of the basic block. For instance x has no
+ expansion in:
+
+ r1 <- r1 + 3
+ x <- r1 + 8
+
+ REGS_ACTIVE is a scratch bitmap that should be clear when passing in.
+ It is clear on return. */
+
+rtx
+cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
+{
+ rtx copy, scopy;
+ int i, j;
+ RTX_CODE code;
+ const char *format_ptr;
+ enum machine_mode mode;
+
+ code = GET_CODE (orig);
+
+ /* For the context of dse, if we end up expand into a huge tree, we
+ will not have a useful address, so we might as well just give up
+ quickly. */
+ if (max_depth <= 0)
+ return NULL;
+
+ switch (code)
+ {
+ case REG:
+ {
+ struct elt_list *l = REG_VALUES (REGNO (orig));
+
+ if (l && l->elt == NULL)
+ l = l->next;
+ for (; l; l = l->next)
+ if (GET_MODE (l->elt->val_rtx) == GET_MODE (orig))
+ {
+ rtx result;
+ int regno = REGNO (orig);
+
+ /* The only thing that we are not willing to do (this
+ is requirement of dse and if others potential uses
+ need this function we should add a parm to control
+ it) is that we will not substitute the
+ STACK_POINTER_REGNUM, FRAME_POINTER or the
+ HARD_FRAME_POINTER.
+
+ These expansions confuses the code that notices that
+ stores into the frame go dead at the end of the
+ function and that the frame is not effected by calls
+ to subroutines. If you allow the
+ STACK_POINTER_REGNUM substitution, then dse will
+ think that parameter pushing also goes dead which is
+ wrong. If you allow the FRAME_POINTER or the
+ HARD_FRAME_POINTER then you lose the opportunity to
+ make the frame assumptions. */
+ if (regno == STACK_POINTER_REGNUM
+ || regno == FRAME_POINTER_REGNUM
+ || regno == HARD_FRAME_POINTER_REGNUM)
+ return orig;
+
+ bitmap_set_bit (regs_active, regno);
+
+ if (dump_file)
+ fprintf (dump_file, "expanding: r%d into: ", regno);
+
+ result = expand_loc (l->elt->locs, regs_active, max_depth);
+ bitmap_clear_bit (regs_active, regno);
+
+ if (result)
+ return result;
+ else
+ return orig;
+ }
+ }
+
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST_VECTOR:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ case SCRATCH:
+ /* SCRATCH must be shared because they represent distinct values. */
+ return orig;
+ case CLOBBER:
+ if (REG_P (XEXP (orig, 0)) && HARD_REGISTER_NUM_P (REGNO (XEXP (orig, 0))))
+ return orig;
+ break;
+
+ case CONST:
+ if (shared_const_p (orig))
+ return orig;
+ break;
+
+ case SUBREG:
+ {
+ rtx subreg = cselib_expand_value_rtx (SUBREG_REG (orig), regs_active,
+ max_depth - 1);
+ if (!subreg)
+ return NULL;
+ scopy = simplify_gen_subreg (GET_MODE (orig), subreg,
+ GET_MODE (SUBREG_REG (orig)),
+ SUBREG_BYTE (orig));
+ if (scopy == NULL
+ || (GET_CODE (scopy) == SUBREG
+ && !REG_P (SUBREG_REG (scopy))
+ && !MEM_P (SUBREG_REG (scopy))))
+ return shallow_copy_rtx (orig);
+ return scopy;
+ }
+
+ case VALUE:
+ if (dump_file)
+ fprintf (dump_file, "expanding value %s into: ",
+ GET_MODE_NAME (GET_MODE (orig)));
+
+ return expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth);
+
+ default:
+ break;
+ }
+
+ /* Copy the various flags, fields, and other information. We assume
+ that all fields need copying, and then clear the fields that should
+ not be copied. That is the sensible default behavior, and forces
+ us to explicitly document why we are *not* copying a flag. */
+ copy = shallow_copy_rtx (orig);
+
+ format_ptr = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ switch (*format_ptr++)
+ {
+ case 'e':
+ if (XEXP (orig, i) != NULL)
+ {
+ rtx result = cselib_expand_value_rtx (XEXP (orig, i), regs_active, max_depth - 1);
+ if (!result)
+ return NULL;
+ XEXP (copy, i) = result;
+ }
+ break;
+
+ case 'E':
+ case 'V':
+ if (XVEC (orig, i) != NULL)
+ {
+ XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
+ for (j = 0; j < XVECLEN (copy, i); j++)
+ {
+ rtx result = cselib_expand_value_rtx (XVECEXP (orig, i, j), regs_active, max_depth - 1);
+ if (!result)
+ return NULL;
+ XVECEXP (copy, i, j) = result;
+ }
+ }
+ break;
+
+ case 't':
+ case 'w':
+ case 'i':
+ case 's':
+ case 'S':
+ case 'T':
+ case 'u':
+ case 'B':
+ case '0':
+ /* These are left unchanged. */
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ mode = GET_MODE (copy);
+ /* If an operand has been simplified into CONST_INT, which doesn't
+ have a mode and the mode isn't derivable from whole rtx's mode,
+ try simplify_*_operation first with mode from original's operand
+ and as a fallback wrap CONST_INT into gen_rtx_CONST. */
+ scopy = copy;
+ switch (GET_RTX_CLASS (code))
+ {
+ case RTX_UNARY:
+ if (CONST_INT_P (XEXP (copy, 0))
+ && GET_MODE (XEXP (orig, 0)) != VOIDmode)
+ {
+ scopy = simplify_unary_operation (code, mode, XEXP (copy, 0),
+ GET_MODE (XEXP (orig, 0)));
+ if (scopy)
+ return scopy;
+ }
+ break;
+ case RTX_COMM_ARITH:
+ case RTX_BIN_ARITH:
+ /* These expressions can derive operand modes from the whole rtx's mode. */
+ break;
+ case RTX_TERNARY:
+ case RTX_BITFIELD_OPS:
+ if (CONST_INT_P (XEXP (copy, 0))
+ && GET_MODE (XEXP (orig, 0)) != VOIDmode)
+ {
+ scopy = simplify_ternary_operation (code, mode,
+ GET_MODE (XEXP (orig, 0)),
+ XEXP (copy, 0), XEXP (copy, 1),
+ XEXP (copy, 2));
+ if (scopy)
+ return scopy;
+ }
+ break;
+ case RTX_COMPARE:
+ case RTX_COMM_COMPARE:
+ if (CONST_INT_P (XEXP (copy, 0))
+ && GET_MODE (XEXP (copy, 1)) == VOIDmode
+ && (GET_MODE (XEXP (orig, 0)) != VOIDmode
+ || GET_MODE (XEXP (orig, 1)) != VOIDmode))
+ {
+ scopy = simplify_relational_operation (code, mode,
+ (GET_MODE (XEXP (orig, 0))
+ != VOIDmode)
+ ? GET_MODE (XEXP (orig, 0))
+ : GET_MODE (XEXP (orig, 1)),
+ XEXP (copy, 0),
+ XEXP (copy, 1));
+ if (scopy)
+ return scopy;
+ }
+ break;
+ default:
+ break;
+ }
+ if (scopy == NULL_RTX)
+ {
+ XEXP (copy, 0)
+ = gen_rtx_CONST (GET_MODE (XEXP (orig, 0)), XEXP (copy, 0));
+ if (dump_file)
+ fprintf (dump_file, " wrapping const_int result in const to preserve mode %s\n",
+ GET_MODE_NAME (GET_MODE (XEXP (copy, 0))));
+ }
+ scopy = simplify_rtx (copy);
+ if (scopy)
+ return scopy;
+ return copy;
+}
+
/* Walk rtx X and replace all occurrences of REG and MEM subexpressions
with VALUE expressions. This way, it becomes independent of changes
to registers and memory.
if (l && l->elt == NULL)
l = l->next;
for (; l; l = l->next)
- if (GET_MODE (l->elt->u.val_rtx) == GET_MODE (x))
- return l->elt->u.val_rtx;
+ if (GET_MODE (l->elt->val_rtx) == GET_MODE (x))
+ return l->elt->val_rtx;
gcc_unreachable ();
match any other. */
e = new_cselib_val (++next_unknown_value, GET_MODE (x));
}
- return e->u.val_rtx;
+ return e->val_rtx;
case CONST_DOUBLE:
case CONST_VECTOR:
case CONST_INT:
+ case CONST_FIXED:
return x;
case POST_INC:
case POST_MODIFY:
case PRE_MODIFY:
e = new_cselib_val (++next_unknown_value, GET_MODE (x));
- return e->u.val_rtx;
+ return e->val_rtx;
default:
break;
if (l && l->elt == NULL)
l = l->next;
for (; l; l = l->next)
- if (mode == GET_MODE (l->elt->u.val_rtx))
+ if (mode == GET_MODE (l->elt->val_rtx))
return l->elt;
if (! create)
REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL);
}
REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
- slot = htab_find_slot_with_hash (hash_table, x, e->value, INSERT);
+ slot = htab_find_slot_with_hash (cselib_hash_table, x, e->value, INSERT);
*slot = e;
return e;
}
if (MEM_P (x))
return cselib_lookup_mem (x, create);
- hashval = cselib_hash_rtx (x, mode, create);
+ hashval = cselib_hash_rtx (x, create);
/* Can't even create if hashing is not possible. */
if (! hashval)
return 0;
- slot = htab_find_slot_with_hash (hash_table, wrap_constant (mode, x),
+ slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
hashval, create ? INSERT : NO_INSERT);
if (slot == 0)
return 0;
else
i = regno - max_value_regs;
- endregno = regno + hard_regno_nregs[regno][mode];
+ endregno = end_hard_regno (mode, regno);
}
else
{
unsigned int this_last = i;
if (i < FIRST_PSEUDO_REGISTER && v != NULL)
- this_last += hard_regno_nregs[i][GET_MODE (v->u.val_rtx)] - 1;
+ this_last = end_hard_regno (GET_MODE (v->val_rtx), i) - 1;
if (this_last < regno || v == NULL)
{
executions of the program. 0 means X can be compared reliably
against certain constants or near-constants. */
-static int
-cselib_rtx_varies_p (rtx x ATTRIBUTE_UNUSED, int from_alias ATTRIBUTE_UNUSED)
+static bool
+cselib_rtx_varies_p (const_rtx x ATTRIBUTE_UNUSED, bool from_alias ATTRIBUTE_UNUSED)
{
/* We actually don't need to verify very hard. This is because
if X has actually changed, we invalidate the memory anyway,
}
if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS)
&& ! canon_true_dependence (mem_rtx, GET_MODE (mem_rtx), mem_addr,
- x, cselib_rtx_varies_p))
+ x, NULL_RTX, cselib_rtx_varies_p))
{
has_mem = true;
num_mems++;
void
cselib_invalidate_rtx (rtx dest)
{
- while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SIGN_EXTRACT
- || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG)
+ while (GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (REG_P (dest))
/* A wrapper for cselib_invalidate_rtx to be called via note_stores. */
static void
-cselib_invalidate_rtx_note_stores (rtx dest, rtx ignore ATTRIBUTE_UNUSED,
+cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
cselib_invalidate_rtx (dest);
}
}
+ if (n_sets == 1
+ && MEM_P (sets[0].src)
+ && !cselib_record_memory
+ && MEM_READONLY_P (sets[0].src))
+ {
+ rtx note = find_reg_equal_equiv_note (insn);
+
+ if (note && CONSTANT_P (XEXP (note, 0)))
+ sets[0].src = XEXP (note, 0);
+ }
+
/* Look up the values that are read. Do this before invalidating the
locations that are written. */
for (i = 0; i < n_sets; i++)
{
rtx src = sets[i].src;
if (cond)
- src = gen_rtx_IF_THEN_ELSE (GET_MODE (src), cond, src, dest);
+ src = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), cond, src, dest);
sets[i].src_elt = cselib_lookup (src, GET_MODE (dest), 1);
if (MEM_P (dest))
sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), Pmode, 1);
int i;
rtx x;
- if (find_reg_note (insn, REG_LIBCALL, NULL))
- cselib_current_insn_in_libcall = true;
- if (find_reg_note (insn, REG_RETVAL, NULL))
- cselib_current_insn_in_libcall = false;
cselib_current_insn = insn;
/* Forget everything at a CODE_LABEL, a volatile asm, or a setjmp. */
&& GET_CODE (PATTERN (insn)) == ASM_OPERANDS
&& MEM_VOLATILE_P (PATTERN (insn))))
{
- clear_table ();
+ cselib_clear_table ();
return;
}
if (CALL_P (insn))
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i])
+ if (call_used_regs[i]
+ || (REG_VALUES (i) && REG_VALUES (i)->elt
+ && HARD_REGNO_CALL_PART_CLOBBERED (i,
+ GET_MODE (REG_VALUES (i)->elt->val_rtx))))
cselib_invalidate_regno (i, reg_raw_mode[i]);
- if (! CONST_OR_PURE_CALL_P (insn))
+ /* Since it is not clear how cselib is going to be used, be
+ conservative here and treat looping pure or const functions
+ as if they were regular functions. */
+ if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
+ || !(RTL_CONST_OR_PURE_CALL_P (insn)))
cselib_invalidate_mem (callmem);
}
cselib_current_insn = 0;
- if (n_useless_values > MAX_USELESS_VALUES)
+ if (n_useless_values > MAX_USELESS_VALUES
+ /* remove_useless_values is linear in the hash table size. Avoid
+ quadratic behavior for very large hashtables with very few
+ useless elements. */
+ && (unsigned int)n_useless_values > cselib_hash_table->n_elements / 4)
remove_useless_values ();
}
sizeof (struct elt_loc_list), 10);
cselib_val_pool = create_alloc_pool ("cselib_val_list",
sizeof (cselib_val), 10);
- value_pool = create_alloc_pool ("value",
- RTX_SIZE (VALUE), 100);
+ value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100);
cselib_record_memory = record_memory;
- /* This is only created once. */
+
+ /* (mem:BLK (scratch)) is a special mechanism to conflict with everything,
+ see canon_true_dependence. This is only created once. */
if (! callmem)
- callmem = gen_rtx_MEM (BLKmode, const0_rtx);
+ callmem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
cselib_nregs = max_reg_num ();
/* Some space for newly emit instructions so we don't end up
reallocating in between passes. */
reg_values_size = cselib_nregs + (63 + cselib_nregs) / 16;
- reg_values = xcalloc (reg_values_size, sizeof (reg_values));
+ reg_values = XCNEWVEC (struct elt_list *, reg_values_size);
}
- used_regs = xmalloc (sizeof (*used_regs) * cselib_nregs);
+ used_regs = XNEWVEC (unsigned int, cselib_nregs);
n_used_regs = 0;
- hash_table = htab_create (31, get_value_hash, entry_and_rtx_equal_p, NULL);
- cselib_current_insn_in_libcall = false;
+ cselib_hash_table = htab_create (31, get_value_hash,
+ entry_and_rtx_equal_p, NULL);
}
/* Called when the current user is done with cselib. */
void
cselib_finish (void)
{
+ cselib_discard_hook = NULL;
free_alloc_pool (elt_list_pool);
free_alloc_pool (elt_loc_list_pool);
free_alloc_pool (cselib_val_pool);
free_alloc_pool (value_pool);
- clear_table ();
- htab_delete (hash_table);
+ cselib_clear_table ();
+ htab_delete (cselib_hash_table);
free (used_regs);
used_regs = 0;
- hash_table = 0;
+ cselib_hash_table = 0;
n_useless_values = 0;
next_unknown_value = 0;
}