/* Common subexpression elimination library for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tm_p.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
-#include "obstack.h"
#include "hashtab.h"
#include "cselib.h"
-static int entry_and_rtx_equal_p PARAMS ((const void *, const void *));
-static unsigned int get_value_hash PARAMS ((const void *));
-static struct elt_list *new_elt_list PARAMS ((struct elt_list *,
- cselib_val *));
-static struct elt_loc_list *new_elt_loc_list PARAMS ((struct elt_loc_list *,
- rtx));
-static void unchain_one_value PARAMS ((cselib_val *));
-static void unchain_one_elt_list PARAMS ((struct elt_list **));
-static void unchain_one_elt_loc_list PARAMS ((struct elt_loc_list **));
-static void clear_table PARAMS ((int));
-static int discard_useless_locs PARAMS ((void **, void *));
-static int discard_useless_values PARAMS ((void **, void *));
-static void remove_useless_values PARAMS ((void));
-static rtx wrap_constant PARAMS ((enum machine_mode, rtx));
-static unsigned int hash_rtx PARAMS ((rtx, enum machine_mode, int));
-static cselib_val *new_cselib_val PARAMS ((unsigned int,
- enum machine_mode));
-static void add_mem_for_addr PARAMS ((cselib_val *, cselib_val *,
- rtx));
-static cselib_val *cselib_lookup_mem PARAMS ((rtx, int));
-static void cselib_invalidate_regno PARAMS ((unsigned int,
- enum machine_mode));
-static int cselib_mem_conflict_p PARAMS ((rtx, rtx));
-static int cselib_invalidate_mem_1 PARAMS ((void **, void *));
-static void cselib_invalidate_mem PARAMS ((rtx));
-static void cselib_invalidate_rtx PARAMS ((rtx, rtx, void *));
-static void cselib_record_set PARAMS ((rtx, cselib_val *,
- cselib_val *));
-static void cselib_record_sets PARAMS ((rtx));
+static int entry_and_rtx_equal_p (const void *, const void *);
+static hashval_t get_value_hash (const void *);
+static struct elt_list *new_elt_list (struct elt_list *, cselib_val *);
+static struct elt_loc_list *new_elt_loc_list (struct elt_loc_list *, rtx);
+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 hash_rtx (rtx, enum machine_mode, 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);
+static void cselib_invalidate_regno (unsigned int, enum machine_mode);
+static int cselib_mem_conflict_p (rtx, rtx);
+static void cselib_invalidate_mem (rtx);
+static void cselib_invalidate_rtx (rtx, rtx, void *);
+static void cselib_record_set (rtx, cselib_val *, cselib_val *);
+static void cselib_record_sets (rtx);
/* There are three ways in which cselib can look up an rtx:
- for a REG, the reg_values table (which is indexed by regno) is used
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 GTY((param_is (cselib_val))) htab_t 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;
/* Number of useless values before we remove them from the hash table. */
#define MAX_USELESS_VALUES 32
-/* This table maps from register number to values. It does not contain
- pointers to cselib_val structures, but rather elt_lists. The purpose is
- to be able to refer to the same register in different modes. */
-static varray_type reg_values;
+/* This table maps from register number to values. It does not
+ contain pointers to cselib_val structures, but rather elt_lists.
+ The purpose is to be able to refer to the same register in
+ different modes. The first element of the list defines the mode in
+ 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. */
+static GTY(()) varray_type reg_values;
+static GTY((deletable (""))) varray_type reg_values_old;
#define REG_VALUES(I) VARRAY_ELT_LIST (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. */
+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. */
-static varray_type used_regs;
+static GTY(()) varray_type used_regs;
+static GTY((deletable (""))) varray_type used_regs_old;
/* We pass this to cselib_invalidate_mem to invalidate all of
memory for a non-const call instruction. */
-static rtx callmem;
-
-/* Memory for our structures is allocated from this obstack. */
-static struct obstack cselib_obstack;
-
-/* Used to quickly free all memory. */
-static char *cselib_startobj;
+static GTY(()) rtx callmem;
/* Caches for unused structures. */
-static cselib_val *empty_vals;
-static struct elt_list *empty_elt_lists;
-static struct elt_loc_list *empty_elt_loc_lists;
+static GTY((deletable (""))) cselib_val *empty_vals;
+static GTY((deletable (""))) struct elt_list *empty_elt_lists;
+static GTY((deletable (""))) struct elt_loc_list *empty_elt_loc_lists;
/* Set by discard_useless_locs if it deleted the last location of any
value. */
static int values_became_useless;
+
+/* Used as stop element of the containing_mem list so we can check
+ presence in the list by checking the next pointer. */
+static cselib_val dummy_val;
+
+/* Used to list all values that contain memory reference.
+ May or may not contain the useless values - the list is compacted
+ each time memory is invalidated. */
+static cselib_val *first_containing_mem = &dummy_val;
\f
/* Allocate a struct elt_list and fill in its two elements with the
arguments. */
static struct elt_list *
-new_elt_list (next, elt)
- struct elt_list *next;
- cselib_val *elt;
+new_elt_list (struct elt_list *next, cselib_val *elt)
{
struct elt_list *el = empty_elt_lists;
if (el)
empty_elt_lists = el->next;
else
- el = (struct elt_list *) obstack_alloc (&cselib_obstack,
- sizeof (struct elt_list));
+ el = ggc_alloc (sizeof (struct elt_list));
el->next = next;
el->elt = elt;
return el;
arguments. */
static struct elt_loc_list *
-new_elt_loc_list (next, loc)
- struct elt_loc_list *next;
- rtx loc;
+new_elt_loc_list (struct elt_loc_list *next, rtx loc)
{
struct elt_loc_list *el = empty_elt_loc_lists;
if (el)
empty_elt_loc_lists = el->next;
else
- el = (struct elt_loc_list *) obstack_alloc (&cselib_obstack,
- sizeof (struct elt_loc_list));
+ el = ggc_alloc (sizeof (struct elt_loc_list));
el->next = next;
el->loc = loc;
el->setting_insn = cselib_current_insn;
+ el->in_libcall = cselib_current_insn_in_libcall;
return el;
}
storage. */
static void
-unchain_one_elt_list (pl)
- struct elt_list **pl;
+unchain_one_elt_list (struct elt_list **pl)
{
struct elt_list *l = *pl;
/* Likewise for elt_loc_lists. */
static void
-unchain_one_elt_loc_list (pl)
- struct elt_loc_list **pl;
+unchain_one_elt_loc_list (struct elt_loc_list **pl)
{
struct elt_loc_list *l = *pl;
V. */
static void
-unchain_one_value (v)
- cselib_val *v;
+unchain_one_value (cselib_val *v)
{
while (v->addr_list)
unchain_one_elt_list (&v->addr_list);
which are known to have been used. */
static void
-clear_table (clear_all)
- int clear_all;
+clear_table (void)
{
unsigned int i;
- if (clear_all)
- for (i = 0; i < cselib_nregs; i++)
- REG_VALUES (i) = 0;
- else
- for (i = 0; i < VARRAY_ACTIVE_SIZE (used_regs); i++)
- REG_VALUES (VARRAY_UINT (used_regs, i)) = 0;
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (used_regs); i++)
+ REG_VALUES (VARRAY_UINT (used_regs, i)) = 0;
+
+ max_value_regs = 0;
VARRAY_POP_ALL (used_regs);
htab_empty (hash_table);
- obstack_free (&cselib_obstack, cselib_startobj);
- empty_vals = 0;
- empty_elt_lists = 0;
- empty_elt_loc_lists = 0;
n_useless_values = 0;
next_unknown_value = 0;
+
+ first_containing_mem = &dummy_val;
}
/* The equality test for our hash table. The first argument ENTRY is a table
CONST of an appropriate mode. */
static int
-entry_and_rtx_equal_p (entry, x_arg)
- const void *entry, *x_arg;
+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;
&& (GET_CODE (XEXP (x, 0)) == CONST_INT
|| GET_CODE (XEXP (x, 0)) == CONST_DOUBLE))
x = XEXP (x, 0);
-
+
/* We don't guarantee that distinct rtx's have different hash values,
so we need to do a comparison. */
for (l = v->locs; l; l = l->next)
hash_rtx when adding an element; this function just extracts the hash
value from a cselib_val structure. */
-static unsigned int
-get_value_hash (entry)
- const void *entry;
+static hashval_t
+get_value_hash (const void *entry)
{
const cselib_val *v = (const cselib_val *) entry;
return v->value;
removed. */
int
-references_value_p (x, only_useless)
- rtx x;
- int only_useless;
+references_value_p (rtx x, int only_useless)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
htab_traverse. */
static int
-discard_useless_locs (x, info)
- void **x;
- void *info ATTRIBUTE_UNUSED;
+discard_useless_locs (void **x, void *info ATTRIBUTE_UNUSED)
{
cselib_val *v = (cselib_val *)*x;
struct elt_loc_list **p = &v->locs;
/* If X is a value with no locations, remove it from the hashtable. */
static int
-discard_useless_values (x, info)
- void **x;
- void *info ATTRIBUTE_UNUSED;
+discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED)
{
cselib_val *v = (cselib_val *)*x;
associated with them) from the hash table. */
static void
-remove_useless_values ()
+remove_useless_values (void)
{
+ cselib_val **p, *v;
/* First pass: eliminate locations that reference the value. That in
turn can make more values useless. */
do
/* Second pass: actually remove the values. */
htab_traverse (hash_table, discard_useless_values, 0);
+ p = &first_containing_mem;
+ for (v = *p; v != &dummy_val; v = v->next_containing_mem)
+ if (v->locs)
+ {
+ *p = v;
+ p = &(*p)->next_containing_mem;
+ }
+ *p = &dummy_val;
+
if (n_useless_values != 0)
abort ();
}
+/* Return the mode in which a register was last set. If X is not a
+ register, return its mode. If the mode in which the register was
+ set is not known, or the value was already clobbered, return
+ VOIDmode. */
+
+enum machine_mode
+cselib_reg_set_mode (rtx x)
+{
+ if (GET_CODE (x) != REG)
+ return GET_MODE (x);
+
+ if (REG_VALUES (REGNO (x)) == NULL
+ || REG_VALUES (REGNO (x))->elt == NULL)
+ return VOIDmode;
+
+ return GET_MODE (REG_VALUES (REGNO (x))->elt->u.val_rtx);
+}
+
/* Return nonzero if we can prove that X and Y contain the same value, taking
our gathered information into account. */
int
-rtx_equal_for_cselib_p (x, y)
- rtx x, y;
+rtx_equal_for_cselib_p (rtx x, rtx y)
{
enum rtx_code code;
const char *fmt;
int i;
-
+
if (GET_CODE (x) == REG || GET_CODE (x) == MEM)
{
cselib_val *e = cselib_lookup (x, GET_MODE (x), 0);
else if (rtx_equal_for_cselib_p (t, y))
return 1;
}
-
+
return 0;
}
else if (rtx_equal_for_cselib_p (x, t))
return 1;
}
-
+
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);
-
+
code = GET_CODE (x);
fmt = GET_RTX_FORMAT (code);
functions. For that purpose, wrap them in a CONST of the appropriate
mode. */
static rtx
-wrap_constant (mode, x)
- enum machine_mode mode;
- rtx x;
+wrap_constant (enum machine_mode mode, rtx x)
{
if (GET_CODE (x) != CONST_INT
&& (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode))
otherwise the mode of X is used. */
static unsigned int
-hash_rtx (x, mode, create)
- rtx x;
- enum machine_mode mode;
- int create;
+hash_rtx (rtx x, enum machine_mode mode, int create)
{
cselib_val *e;
int i, j;
the integers representing the constant. */
hash += (unsigned) code + (unsigned) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
- for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
- hash += XWINT (x, i);
+ hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
return hash ? hash : (unsigned int) CONST_DOUBLE;
+ case CONST_VECTOR:
+ {
+ int units;
+ rtx elt;
+
+ units = CONST_VECTOR_NUNITS (x);
+
+ for (i = 0; i < units; ++i)
+ {
+ elt = CONST_VECTOR_ELT (x, i);
+ hash += hash_rtx (elt, GET_MODE (elt), 0);
+ }
+
+ return hash;
+ }
+
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
hash
return 0;
break;
-
+
default:
break;
}
value is MODE. */
static cselib_val *
-new_cselib_val (value, mode)
- unsigned int value;
- enum machine_mode mode;
+new_cselib_val (unsigned int value, enum machine_mode mode)
{
cselib_val *e = empty_vals;
if (e)
empty_vals = e->u.next_free;
else
- e = (cselib_val *) obstack_alloc (&cselib_obstack, sizeof (cselib_val));
+ e = ggc_alloc (sizeof (cselib_val));
if (value == 0)
abort ();
CSELIB_VAL_PTR (e->u.val_rtx) = e;
e->addr_list = 0;
e->locs = 0;
+ e->next_containing_mem = 0;
return e;
}
value. Update the two value structures to represent this situation. */
static void
-add_mem_for_addr (addr_elt, mem_elt, x)
- cselib_val *addr_elt, *mem_elt;
- rtx x;
+add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
{
struct elt_loc_list *l;
mem_elt->locs
= new_elt_loc_list (mem_elt->locs,
replace_equiv_address_nv (x, addr_elt->u.val_rtx));
+ if (mem_elt->next_containing_mem == NULL)
+ {
+ mem_elt->next_containing_mem = first_containing_mem;
+ first_containing_mem = mem_elt;
+ }
}
/* Subroutine of cselib_lookup. Return a value for X, which is a MEM rtx.
If CREATE, make a new one if we haven't seen it before. */
static cselib_val *
-cselib_lookup_mem (x, create)
- rtx x;
- int create;
+cselib_lookup_mem (rtx x, int create)
{
enum machine_mode mode = GET_MODE (x);
void **slot;
allocated. However, the return value can share rtl with X. */
rtx
-cselib_subst_to_values (x)
- rtx x;
+cselib_subst_to_values (rtx x)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
switch (code)
{
case REG:
- for (l = REG_VALUES (REGNO (x)); l; l = l->next)
+ l = REG_VALUES (REGNO (x));
+ 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;
}
return e->u.val_rtx;
- /* CONST_DOUBLEs must be special-cased here so that we won't try to
- look up the CONST_DOUBLE_MEM inside. */
case CONST_DOUBLE:
+ case CONST_VECTOR:
case CONST_INT:
return x;
case PRE_MODIFY:
e = new_cselib_val (++next_unknown_value, GET_MODE (x));
return e->u.val_rtx;
-
+
default:
break;
}
(i.e. because it's a constant). */
cselib_val *
-cselib_lookup (x, mode, create)
- rtx x;
- enum machine_mode mode;
- int create;
+cselib_lookup (rtx x, enum machine_mode mode, int create)
{
void **slot;
cselib_val *e;
struct elt_list *l;
unsigned int i = REGNO (x);
- for (l = REG_VALUES (i); l; l = l->next)
+ l = REG_VALUES (i);
+ if (l && l->elt == NULL)
+ l = l->next;
+ for (; l; l = l->next)
if (mode == GET_MODE (l->elt->u.val_rtx))
return l->elt;
if (! create)
return 0;
+ if (i < FIRST_PSEUDO_REGISTER)
+ {
+ unsigned int n = HARD_REGNO_NREGS (i, mode);
+
+ if (n > max_value_regs)
+ max_value_regs = n;
+ }
+
e = new_cselib_val (++next_unknown_value, GET_MODE (x));
e->locs = new_elt_loc_list (e->locs, x);
if (REG_VALUES (i) == 0)
- VARRAY_PUSH_UINT (used_regs, i);
- REG_VALUES (i) = new_elt_list (REG_VALUES (i), e);
+ {
+ /* Maintain the invariant that the first entry of
+ REG_VALUES, if present, must be the value used to set the
+ register, or NULL. */
+ VARRAY_PUSH_UINT (used_regs, i);
+ 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 = e;
return e;
invalidating call clobbered registers across a call. */
static void
-cselib_invalidate_regno (regno, mode)
- unsigned int regno;
- enum machine_mode mode;
+cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
{
unsigned int endregno;
unsigned int i;
pseudos, only REGNO is affected. For hard regs, we must take MODE
into account, and we must also invalidate lower register numbers
if they contain values that overlap REGNO. */
- endregno = regno + 1;
- if (regno < FIRST_PSEUDO_REGISTER && mode != VOIDmode)
- endregno = regno + HARD_REGNO_NREGS (regno, mode);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ if (mode == VOIDmode)
+ abort ();
+
+ if (regno < max_value_regs)
+ i = 0;
+ else
+ i = regno - max_value_regs;
- for (i = 0; i < endregno; i++)
+ endregno = regno + HARD_REGNO_NREGS (regno, mode);
+ }
+ else
+ {
+ i = regno;
+ endregno = regno + 1;
+ }
+
+ for (; i < endregno; i++)
{
struct elt_list **l = ®_VALUES (i);
struct elt_loc_list **p;
unsigned int this_last = i;
- if (i < FIRST_PSEUDO_REGISTER)
+ if (i < FIRST_PSEUDO_REGISTER && v != NULL)
this_last += HARD_REGNO_NREGS (i, GET_MODE (v->u.val_rtx)) - 1;
- if (this_last < regno)
+ if (this_last < regno || v == NULL)
{
l = &(*l)->next;
continue;
}
/* We have an overlap. */
- unchain_one_elt_list (l);
+ if (*l == REG_VALUES (i))
+ {
+ /* Maintain the invariant that the first entry of
+ REG_VALUES, if present, must be the value used to set
+ the register, or NULL. This is also nice because
+ then we won't push the same regno onto user_regs
+ multiple times. */
+ (*l)->elt = NULL;
+ l = &(*l)->next;
+ }
+ else
+ unchain_one_elt_list (l);
/* Now, we clear the mapping from value to reg. It must exist, so
this code will crash intentionally if it doesn't. */
Return whether this change will invalidate VAL. */
static int
-cselib_mem_conflict_p (mem_base, val)
- rtx mem_base;
- rtx val;
+cselib_mem_conflict_p (rtx mem_base, rtx val)
{
enum rtx_code code;
const char *fmt;
case CONST:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
return 0;
return 0;
}
-/* For the value found in SLOT, walk its locations to determine if any overlap
- INFO (which is a MEM rtx). */
+/* Invalidate any locations in the table which are changed because of a
+ store to MEM_RTX. If this is called because of a non-const call
+ instruction, MEM_RTX is (mem:BLK const0_rtx). */
-static int
-cselib_invalidate_mem_1 (slot, info)
- void **slot;
- void *info;
+static void
+cselib_invalidate_mem (rtx mem_rtx)
{
- cselib_val *v = (cselib_val *) *slot;
- rtx mem_rtx = (rtx) info;
- struct elt_loc_list **p = &v->locs;
- int had_locs = v->locs != 0;
+ cselib_val **vp, *v, *next;
- while (*p)
+ vp = &first_containing_mem;
+ for (v = *vp; v != &dummy_val; v = next)
{
- rtx x = (*p)->loc;
- cselib_val *addr;
- struct elt_list **mem_chain;
-
- /* MEMs may occur in locations only at the top level; below
- that every MEM or REG is substituted by its VALUE. */
- if (GET_CODE (x) != MEM
- || ! cselib_mem_conflict_p (mem_rtx, x))
- {
- p = &(*p)->next;
- continue;
- }
+ bool has_mem = false;
+ struct elt_loc_list **p = &v->locs;
+ int had_locs = v->locs != 0;
- /* This one overlaps. */
- /* We must have a mapping from this MEM's address to the
- value (E). Remove that, too. */
- addr = cselib_lookup (XEXP (x, 0), VOIDmode, 0);
- mem_chain = &addr->addr_list;
- for (;;)
+ while (*p)
{
- if ((*mem_chain)->elt == v)
+ rtx x = (*p)->loc;
+ cselib_val *addr;
+ struct elt_list **mem_chain;
+
+ /* MEMs may occur in locations only at the top level; below
+ that every MEM or REG is substituted by its VALUE. */
+ if (GET_CODE (x) != MEM)
{
- unchain_one_elt_list (mem_chain);
- break;
+ p = &(*p)->next;
+ continue;
+ }
+ if (! cselib_mem_conflict_p (mem_rtx, x))
+ {
+ has_mem = true;
+ p = &(*p)->next;
+ continue;
}
- mem_chain = &(*mem_chain)->next;
- }
-
- unchain_one_elt_loc_list (p);
- }
+ /* This one overlaps. */
+ /* We must have a mapping from this MEM's address to the
+ value (E). Remove that, too. */
+ addr = cselib_lookup (XEXP (x, 0), VOIDmode, 0);
+ mem_chain = &addr->addr_list;
+ for (;;)
+ {
+ if ((*mem_chain)->elt == v)
+ {
+ unchain_one_elt_list (mem_chain);
+ break;
+ }
- if (had_locs && v->locs == 0)
- n_useless_values++;
+ mem_chain = &(*mem_chain)->next;
+ }
- return 1;
-}
+ unchain_one_elt_loc_list (p);
+ }
-/* Invalidate any locations in the table which are changed because of a
- store to MEM_RTX. If this is called because of a non-const call
- instruction, MEM_RTX is (mem:BLK const0_rtx). */
+ if (had_locs && v->locs == 0)
+ n_useless_values++;
-static void
-cselib_invalidate_mem (mem_rtx)
- rtx mem_rtx;
-{
- htab_traverse (hash_table, cselib_invalidate_mem_1, mem_rtx);
+ next = v->next_containing_mem;
+ if (has_mem)
+ {
+ *vp = v;
+ vp = &(*vp)->next_containing_mem;
+ }
+ else
+ v->next_containing_mem = NULL;
+ }
+ *vp = &dummy_val;
}
/* Invalidate DEST, which is being assigned to or clobbered. The second and
note_stores; they are ignored. */
static void
-cselib_invalidate_rtx (dest, ignore, data)
- rtx dest;
- rtx ignore ATTRIBUTE_UNUSED;
- void *data ATTRIBUTE_UNUSED;
+cselib_invalidate_rtx (rtx dest, rtx ignore ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
{
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG)
describes its address. */
static void
-cselib_record_set (dest, src_elt, dest_addr_elt)
- rtx dest;
- cselib_val *src_elt, *dest_addr_elt;
+cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
{
int dreg = GET_CODE (dest) == REG ? (int) REGNO (dest) : -1;
if (dreg >= 0)
{
+ if (dreg < FIRST_PSEUDO_REGISTER)
+ {
+ unsigned int n = HARD_REGNO_NREGS (dreg, GET_MODE (dest));
+
+ if (n > max_value_regs)
+ max_value_regs = n;
+ }
+
if (REG_VALUES (dreg) == 0)
- VARRAY_PUSH_UINT (used_regs, dreg);
+ {
+ VARRAY_PUSH_UINT (used_regs, dreg);
+ REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
+ }
+ else
+ {
+ if (REG_VALUES (dreg)->elt == 0)
+ REG_VALUES (dreg)->elt = src_elt;
+ else
+ /* The register should have been invalidated. */
+ abort ();
+ }
- REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
if (src_elt->locs == 0)
n_useless_values--;
src_elt->locs = new_elt_loc_list (src_elt->locs, dest);
/* Record the effects of any sets in INSN. */
static void
-cselib_record_sets (insn)
- rtx insn;
+cselib_record_sets (rtx insn)
{
int n_sets = 0;
int i;
rtx src = sets[i].src;
if (cond)
src = gen_rtx_IF_THEN_ELSE (GET_MODE (src), cond, src, dest);
- sets[i].src_elt = cselib_lookup (sets[i].src, GET_MODE (dest), 1);
+ sets[i].src_elt = cselib_lookup (src, GET_MODE (dest), 1);
if (GET_CODE (dest) == MEM)
sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), Pmode, 1);
else
/* Record the effects of INSN. */
void
-cselib_process_insn (insn)
- rtx insn;
+cselib_process_insn (rtx insn)
{
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 (0);
+ clear_table ();
return;
}
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i])
- cselib_invalidate_regno (i, VOIDmode);
+ cselib_invalidate_regno (i, reg_raw_mode[i]);
if (! CONST_OR_PURE_CALL_P (insn))
cselib_invalidate_mem (callmem);
it must be called by the user if it allocated new registers. */
void
-cselib_update_varray_sizes ()
+cselib_update_varray_sizes (void)
{
unsigned int nregs = max_reg_num ();
init_alias_analysis. */
void
-cselib_init ()
+cselib_init (void)
{
- /* These are only created once. */
+ /* This is only created once. */
if (! callmem)
- {
- gcc_obstack_init (&cselib_obstack);
- cselib_startobj = obstack_alloc (&cselib_obstack, 0);
-
- callmem = gen_rtx_MEM (BLKmode, const0_rtx);
- ggc_add_rtx_root (&callmem, 1);
- }
+ callmem = gen_rtx_MEM (BLKmode, const0_rtx);
cselib_nregs = max_reg_num ();
- VARRAY_ELT_LIST_INIT (reg_values, cselib_nregs, "reg_values");
- VARRAY_UINT_INIT (used_regs, cselib_nregs, "used_regs");
- hash_table = htab_create (31, get_value_hash, entry_and_rtx_equal_p, NULL);
- clear_table (1);
+ if (reg_values_old != NULL && VARRAY_SIZE (reg_values_old) >= cselib_nregs)
+ {
+ reg_values = reg_values_old;
+ used_regs = used_regs_old;
+ }
+ else
+ {
+ VARRAY_ELT_LIST_INIT (reg_values, cselib_nregs, "reg_values");
+ VARRAY_UINT_INIT (used_regs, cselib_nregs, "used_regs");
+ }
+ hash_table = htab_create_ggc (31, get_value_hash, entry_and_rtx_equal_p,
+ NULL);
+ cselib_current_insn_in_libcall = false;
}
/* Called when the current user is done with cselib. */
void
-cselib_finish ()
+cselib_finish (void)
{
- clear_table (0);
- VARRAY_FREE (reg_values);
- VARRAY_FREE (used_regs);
- htab_delete (hash_table);
+ clear_table ();
+ reg_values_old = reg_values;
+ reg_values = 0;
+ used_regs_old = used_regs;
+ used_regs = 0;
+ hash_table = 0;
+ n_useless_values = 0;
+ next_unknown_value = 0;
}
+
+#include "gt-cselib.h"