#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "function.h"
#include "emit-rtl.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
#include "hashtab.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 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 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, 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 void cselib_invalidate_mem (rtx);
static void cselib_record_set (rtx, cselib_val *, cselib_val *);
static void cselib_record_sets (rtx);
static htab_t cselib_hash_table;
static rtx cselib_current_insn;
static bool cselib_current_insn_in_libcall;
static unsigned int next_unknown_value;
static unsigned int cselib_nregs;
static int n_useless_values;
#define MAX_USELESS_VALUES 32
static struct elt_list **reg_values;
static unsigned int reg_values_size;
#define REG_VALUES(i) reg_values[i]
static unsigned int max_value_regs;
static unsigned int *used_regs;
static unsigned int n_used_regs;
static GTY(()) rtx callmem;
static int values_became_useless;
static cselib_val dummy_val;
static cselib_val *first_containing_mem = &dummy_val;
static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool;
static inline struct elt_list *
new_elt_list (struct elt_list *next, cselib_val *elt)
{
struct elt_list *el;
el = pool_alloc (elt_list_pool);
el->next = next;
el->elt = elt;
return el;
}
static inline struct elt_loc_list *
new_elt_loc_list (struct elt_loc_list *next, rtx loc)
{
struct elt_loc_list *el;
el = 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;
}
static inline void
unchain_one_elt_list (struct elt_list **pl)
{
struct elt_list *l = *pl;
*pl = l->next;
pool_free (elt_list_pool, l);
}
static void
unchain_one_elt_loc_list (struct elt_loc_list **pl)
{
struct elt_loc_list *l = *pl;
*pl = l->next;
pool_free (elt_loc_list_pool, l);
}
static void
unchain_one_value (cselib_val *v)
{
while (v->addr_list)
unchain_one_elt_list (&v->addr_list);
pool_free (cselib_val_pool, v);
}
void
cselib_clear_table (void)
{
unsigned int i;
for (i = 0; i < n_used_regs; i++)
REG_VALUES (used_regs[i]) = 0;
max_value_regs = 0;
n_used_regs = 0;
htab_empty (cselib_hash_table);
n_useless_values = 0;
next_unknown_value = 0;
first_containing_mem = &dummy_val;
}
static int
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;
enum machine_mode mode = GET_MODE (x);
gcc_assert (GET_CODE (x) != CONST_INT
&& (mode != VOIDmode || GET_CODE (x) != CONST_DOUBLE));
if (mode != GET_MODE (v->u.val_rtx))
return 0;
if (GET_CODE (x) == CONST
&& (GET_CODE (XEXP (x, 0)) == CONST_INT
|| GET_CODE (XEXP (x, 0)) == CONST_DOUBLE))
x = XEXP (x, 0);
for (l = v->locs; l; l = l->next)
if (rtx_equal_for_cselib_p (l->loc, x))
return 1;
return 0;
}
static hashval_t
get_value_hash (const void *entry)
{
const cselib_val *v = (const cselib_val *) entry;
return v->value;
}
int
references_value_p (rtx x, int only_useless)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
int i, j;
if (GET_CODE (x) == VALUE
&& (! only_useless || CSELIB_VAL_PTR (x)->locs == 0))
return 1;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e' && references_value_p (XEXP (x, i), only_useless))
return 1;
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
if (references_value_p (XVECEXP (x, i, j), only_useless))
return 1;
}
return 0;
}
static int
discard_useless_locs (void **x, void *info ATTRIBUTE_UNUSED)
{
cselib_val *v = (cselib_val *)*x;
struct elt_loc_list **p = &v->locs;
int had_locs = v->locs != 0;
while (*p)
{
if (references_value_p ((*p)->loc, 1))
unchain_one_elt_loc_list (p);
else
p = &(*p)->next;
}
if (had_locs && v->locs == 0)
{
n_useless_values++;
values_became_useless = 1;
}
return 1;
}
static int
discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED)
{
cselib_val *v = (cselib_val *)*x;
if (v->locs == 0)
{
CSELIB_VAL_PTR (v->u.val_rtx) = NULL;
htab_clear_slot (cselib_hash_table, x);
unchain_one_value (v);
n_useless_values--;
}
return 1;
}
static void
remove_useless_values (void)
{
cselib_val **p, *v;
do
{
values_became_useless = 0;
htab_traverse (cselib_hash_table, discard_useless_locs, 0);
}
while (values_became_useless);
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;
htab_traverse (cselib_hash_table, discard_useless_values, 0);
gcc_assert (!n_useless_values);
}
enum machine_mode
cselib_reg_set_mode (rtx x)
{
if (!REG_P (x))
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);
}
int
rtx_equal_for_cselib_p (rtx x, rtx y)
{
enum rtx_code code;
const char *fmt;
int i;
if (REG_P (x) || MEM_P (x))
{
cselib_val *e = cselib_lookup (x, GET_MODE (x), 0);
if (e)
x = e->u.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;
}
if (x == y)
return 1;
if (GET_CODE (x) == VALUE && GET_CODE (y) == VALUE)
return CSELIB_VAL_PTR (x) == CSELIB_VAL_PTR (y);
if (GET_CODE (x) == VALUE)
{
cselib_val *e = CSELIB_VAL_PTR (x);
struct elt_loc_list *l;
for (l = e->locs; l; l = l->next)
{
rtx t = l->loc;
if (REG_P (t) || MEM_P (t))
continue;
else if (rtx_equal_for_cselib_p (t, y))
return 1;
}
return 0;
}
if (GET_CODE (y) == VALUE)
{
cselib_val *e = CSELIB_VAL_PTR (y);
struct elt_loc_list *l;
for (l = e->locs; l; l = l->next)
{
rtx t = l->loc;
if (REG_P (t) || MEM_P (t))
continue;
else if (rtx_equal_for_cselib_p (x, t))
return 1;
}
return 0;
}
if (GET_CODE (x) != GET_CODE (y) || GET_MODE (x) != GET_MODE (y))
return 0;
switch (GET_CODE (x))
{
case CONST_DOUBLE:
return 0;
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
default:
break;
}
code = GET_CODE (x);
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
int j;
switch (fmt[i])
{
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case 'n':
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 'V':
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
for (j = 0; j < XVECLEN (x, i); j++)
if (! rtx_equal_for_cselib_p (XVECEXP (x, i, j),
XVECEXP (y, i, j)))
return 0;
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;
case 'S':
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'u':
break;
case '0':
case 't':
break;
default:
gcc_unreachable ();
}
}
return 1;
}
static rtx
wrap_constant (enum machine_mode mode, rtx x)
{
if (GET_CODE (x) != CONST_INT
&& (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode))
return x;
gcc_assert (mode != VOIDmode);
return gen_rtx_CONST (mode, x);
}
static unsigned int
cselib_hash_rtx (rtx x, int create)
{
cselib_val *e;
int i, j;
enum rtx_code code;
const char *fmt;
unsigned int hash = 0;
code = GET_CODE (x);
hash += (unsigned) code + (unsigned) GET_MODE (x);
switch (code)
{
case MEM:
case REG:
e = cselib_lookup (x, GET_MODE (x), create);
if (! e)
return 0;
return e->value;
case CONST_INT:
hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
return hash ? hash : (unsigned int) CONST_INT;
case CONST_DOUBLE:
hash += (unsigned) code + (unsigned) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
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 += cselib_hash_rtx (elt, 0);
}
return hash;
}
case LABEL_REF:
hash += (((unsigned int) LABEL_REF << 7)
+ CODE_LABEL_NUMBER (XEXP (x, 0)));
return hash ? hash : (unsigned int) LABEL_REF;
case SYMBOL_REF:
{
unsigned int h = 0;
const unsigned char *p = (const unsigned char *) XSTR (x, 0);
while (*p)
h += (h << 7) + *p++;
hash += ((unsigned int) SYMBOL_REF << 7) + h;
return hash ? hash : (unsigned int) SYMBOL_REF;
}
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
case POST_MODIFY:
case PRE_MODIFY:
case PC:
case CC0:
case CALL:
case UNSPEC_VOLATILE:
return 0;
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
return 0;
break;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
switch (fmt[i])
{
case 'e':
{
rtx tem = XEXP (x, i);
unsigned int tem_hash = cselib_hash_rtx (tem, create);
if (tem_hash == 0)
return 0;
hash += tem_hash;
}
break;
case 'E':
for (j = 0; j < XVECLEN (x, i); j++)
{
unsigned int tem_hash
= cselib_hash_rtx (XVECEXP (x, i, j), create);
if (tem_hash == 0)
return 0;
hash += tem_hash;
}
break;
case 's':
{
const unsigned char *p = (const unsigned char *) XSTR (x, i);
if (p)
while (*p)
hash += *p++;
break;
}
case 'i':
hash += XINT (x, i);
break;
case '0':
case 't':
break;
default:
gcc_unreachable ();
}
}
return hash ? hash : 1 + (unsigned int) GET_CODE (x);
}
static inline cselib_val *
new_cselib_val (unsigned int value, enum machine_mode mode)
{
cselib_val *e = pool_alloc (cselib_val_pool);
gcc_assert (value);
e->value = value;
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;
e->addr_list = 0;
e->locs = 0;
e->next_containing_mem = 0;
return e;
}
static void
add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
{
struct elt_loc_list *l;
for (l = mem_elt->locs; l; l = l->next)
if (MEM_P (l->loc)
&& CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt)
return;
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));
if (mem_elt->next_containing_mem == NULL)
{
mem_elt->next_containing_mem = first_containing_mem;
first_containing_mem = mem_elt;
}
}
static cselib_val *
cselib_lookup_mem (rtx x, int create)
{
enum machine_mode mode = GET_MODE (x);
void **slot;
cselib_val *addr;
cselib_val *mem_elt;
struct elt_list *l;
if (MEM_VOLATILE_P (x) || mode == BLKmode
|| !cselib_record_memory
|| (FLOAT_MODE_P (mode) && flag_float_store))
return 0;
addr = cselib_lookup (XEXP (x, 0), mode, create);
if (! addr)
return 0;
for (l = addr->addr_list; l; l = l->next)
if (GET_MODE (l->elt->u.val_rtx) == mode)
return l->elt;
if (! create)
return 0;
mem_elt = new_cselib_val (++next_unknown_value, mode);
add_mem_for_addr (addr, mem_elt, x);
slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
mem_elt->value, INSERT);
*slot = mem_elt;
return mem_elt;
}
rtx
cselib_subst_to_values (rtx x)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
cselib_val *e;
struct elt_list *l;
rtx copy = x;
int i;
switch (code)
{
case REG:
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;
gcc_unreachable ();
case MEM:
e = cselib_lookup_mem (x, 0);
if (! e)
{
e = new_cselib_val (++next_unknown_value, GET_MODE (x));
}
return e->u.val_rtx;
case CONST_DOUBLE:
case CONST_VECTOR:
case CONST_INT:
return x;
case POST_INC:
case PRE_INC:
case POST_DEC:
case PRE_DEC:
case POST_MODIFY:
case PRE_MODIFY:
e = new_cselib_val (++next_unknown_value, GET_MODE (x));
return e->u.val_rtx;
default:
break;
}
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx t = cselib_subst_to_values (XEXP (x, i));
if (t != XEXP (x, i) && x == copy)
copy = shallow_copy_rtx (x);
XEXP (copy, i) = t;
}
else if (fmt[i] == 'E')
{
int j, k;
for (j = 0; j < XVECLEN (x, i); j++)
{
rtx t = cselib_subst_to_values (XVECEXP (x, i, j));
if (t != XVECEXP (x, i, j) && XVEC (x, i) == XVEC (copy, i))
{
if (x == copy)
copy = shallow_copy_rtx (x);
XVEC (copy, i) = rtvec_alloc (XVECLEN (x, i));
for (k = 0; k < j; k++)
XVECEXP (copy, i, k) = XVECEXP (x, i, k);
}
XVECEXP (copy, i, j) = t;
}
}
}
return copy;
}
cselib_val *
cselib_lookup (rtx x, enum machine_mode mode, int create)
{
void **slot;
cselib_val *e;
unsigned int hashval;
if (GET_MODE (x) != VOIDmode)
mode = GET_MODE (x);
if (GET_CODE (x) == VALUE)
return CSELIB_VAL_PTR (x);
if (REG_P (x))
{
struct elt_list *l;
unsigned int i = REGNO (x);
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)
{
used_regs[n_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 (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, create);
if (! hashval)
return 0;
slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
hashval, create ? INSERT : NO_INSERT);
if (slot == 0)
return 0;
e = (cselib_val *) *slot;
if (e)
return e;
e = new_cselib_val (hashval, mode);
*slot = (void *) e;
e->locs = new_elt_loc_list (e->locs, cselib_subst_to_values (x));
return e;
}
static void
cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
{
unsigned int endregno;
unsigned int i;
gcc_assert (!reload_completed || regno < FIRST_PSEUDO_REGISTER
|| reg_renumber[regno] < 0);
if (regno < FIRST_PSEUDO_REGISTER)
{
gcc_assert (mode != VOIDmode);
if (regno < max_value_regs)
i = 0;
else
i = regno - max_value_regs;
endregno = regno + hard_regno_nregs[regno][mode];
}
else
{
i = regno;
endregno = regno + 1;
}
for (; i < endregno; i++)
{
struct elt_list **l = ®_VALUES (i);
while (*l)
{
cselib_val *v = (*l)->elt;
struct elt_loc_list **p;
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;
if (this_last < regno || v == NULL)
{
l = &(*l)->next;
continue;
}
if (*l == REG_VALUES (i))
{
(*l)->elt = NULL;
l = &(*l)->next;
}
else
unchain_one_elt_list (l);
for (p = &v->locs; ; p = &(*p)->next)
{
rtx x = (*p)->loc;
if (REG_P (x) && REGNO (x) == i)
{
unchain_one_elt_loc_list (p);
break;
}
}
if (v->locs == 0)
n_useless_values++;
}
}
}
static int
cselib_rtx_varies_p (rtx x ATTRIBUTE_UNUSED, int from_alias ATTRIBUTE_UNUSED)
{
return 0;
}
static void
cselib_invalidate_mem (rtx mem_rtx)
{
cselib_val **vp, *v, *next;
int num_mems = 0;
rtx mem_addr;
mem_addr = canon_rtx (get_addr (XEXP (mem_rtx, 0)));
mem_rtx = canon_rtx (mem_rtx);
vp = &first_containing_mem;
for (v = *vp; v != &dummy_val; v = next)
{
bool has_mem = false;
struct elt_loc_list **p = &v->locs;
int had_locs = v->locs != 0;
while (*p)
{
rtx x = (*p)->loc;
cselib_val *addr;
struct elt_list **mem_chain;
if (!MEM_P (x))
{
p = &(*p)->next;
continue;
}
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))
{
has_mem = true;
num_mems++;
p = &(*p)->next;
continue;
}
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;
}
mem_chain = &(*mem_chain)->next;
}
unchain_one_elt_loc_list (p);
}
if (had_locs && v->locs == 0)
n_useless_values++;
next = v->next_containing_mem;
if (has_mem)
{
*vp = v;
vp = &(*vp)->next_containing_mem;
}
else
v->next_containing_mem = NULL;
}
*vp = &dummy_val;
}
void
cselib_invalidate_rtx (rtx dest)
{
while (GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (REG_P (dest))
cselib_invalidate_regno (REGNO (dest), GET_MODE (dest));
else if (MEM_P (dest))
cselib_invalidate_mem (dest);
if (push_operand (dest, GET_MODE (dest)))
cselib_invalidate_rtx (stack_pointer_rtx);
}
static void
cselib_invalidate_rtx_note_stores (rtx dest, rtx ignore ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
cselib_invalidate_rtx (dest);
}
static void
cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
{
int dreg = REG_P (dest) ? (int) REGNO (dest) : -1;
if (src_elt == 0 || side_effects_p (dest))
return;
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)
{
used_regs[n_used_regs++] = dreg;
REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
}
else
{
gcc_assert (REG_VALUES (dreg)->elt == 0);
REG_VALUES (dreg)->elt = src_elt;
}
if (src_elt->locs == 0)
n_useless_values--;
src_elt->locs = new_elt_loc_list (src_elt->locs, dest);
}
else if (MEM_P (dest) && dest_addr_elt != 0
&& cselib_record_memory)
{
if (src_elt->locs == 0)
n_useless_values--;
add_mem_for_addr (dest_addr_elt, src_elt, dest);
}
}
struct set
{
rtx src;
rtx dest;
cselib_val *src_elt;
cselib_val *dest_addr_elt;
};
#define MAX_SETS (FIRST_PSEUDO_REGISTER * 2)
static void
cselib_record_sets (rtx insn)
{
int n_sets = 0;
int i;
struct set sets[MAX_SETS];
rtx body = PATTERN (insn);
rtx cond = 0;
body = PATTERN (insn);
if (GET_CODE (body) == COND_EXEC)
{
cond = COND_EXEC_TEST (body);
body = COND_EXEC_CODE (body);
}
if (GET_CODE (body) == SET)
{
sets[0].src = SET_SRC (body);
sets[0].dest = SET_DEST (body);
n_sets = 1;
}
else if (GET_CODE (body) == PARALLEL)
{
for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
{
rtx x = XVECEXP (body, 0, i);
if (GET_CODE (x) == SET)
{
sets[n_sets].src = SET_SRC (x);
sets[n_sets].dest = SET_DEST (x);
n_sets++;
}
}
}
for (i = 0; i < n_sets; i++)
{
rtx dest = sets[i].dest;
if (GET_CODE (sets[i].dest) == STRICT_LOW_PART)
sets[i].dest = dest = XEXP (dest, 0);
if (REG_P (dest)
|| (MEM_P (dest) && cselib_record_memory))
{
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 (src, GET_MODE (dest), 1);
if (MEM_P (dest))
sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), Pmode, 1);
else
sets[i].dest_addr_elt = 0;
}
}
note_stores (body, cselib_invalidate_rtx_note_stores, NULL);
if (n_sets >= 2 && asm_noperands (body) >= 0)
{
for (i = 0; i < n_sets; i++)
{
rtx dest = sets[i].dest;
if (REG_P (dest) || MEM_P (dest))
{
int j;
for (j = i + 1; j < n_sets; j++)
if (rtx_equal_p (dest, sets[j].dest))
{
sets[i].dest = pc_rtx;
sets[j].dest = pc_rtx;
}
}
}
}
for (i = 0; i < n_sets; i++)
{
rtx dest = sets[i].dest;
if (REG_P (dest)
|| (MEM_P (dest) && cselib_record_memory))
cselib_record_set (dest, sets[i].src_elt, sets[i].dest_addr_elt);
}
}
void
cselib_process_insn (rtx insn)
{
int i;
rtx x;
if (find_reg_note (insn, REG_LIBCALL, NULL))
cselib_current_insn_in_libcall = true;
cselib_current_insn = insn;
if (LABEL_P (insn)
|| (CALL_P (insn)
&& find_reg_note (insn, REG_SETJMP, NULL))
|| (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == ASM_OPERANDS
&& MEM_VOLATILE_P (PATTERN (insn))))
{
if (find_reg_note (insn, REG_RETVAL, NULL))
cselib_current_insn_in_libcall = false;
cselib_clear_table ();
return;
}
if (! INSN_P (insn))
{
if (find_reg_note (insn, REG_RETVAL, NULL))
cselib_current_insn_in_libcall = false;
cselib_current_insn = 0;
return;
}
if (CALL_P (insn))
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; 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->u.val_rtx))))
cselib_invalidate_regno (i, reg_raw_mode[i]);
if (! CONST_OR_PURE_CALL_P (insn))
cselib_invalidate_mem (callmem);
}
cselib_record_sets (insn);
#ifdef AUTO_INC_DEC
for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
if (REG_NOTE_KIND (x) == REG_INC)
cselib_invalidate_rtx (XEXP (x, 0));
#endif
if (CALL_P (insn))
for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
if (GET_CODE (XEXP (x, 0)) == CLOBBER)
cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
if (find_reg_note (insn, REG_RETVAL, NULL))
cselib_current_insn_in_libcall = false;
cselib_current_insn = 0;
if (n_useless_values > MAX_USELESS_VALUES
&& (unsigned int)n_useless_values > cselib_hash_table->n_elements / 4)
remove_useless_values ();
}
void
cselib_init (bool record_memory)
{
elt_list_pool = create_alloc_pool ("elt_list",
sizeof (struct elt_list), 10);
elt_loc_list_pool = create_alloc_pool ("elt_loc_list",
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_CODE_SIZE (VALUE), 100);
cselib_record_memory = record_memory;
if (! callmem)
callmem = gen_rtx_MEM (BLKmode, const0_rtx);
cselib_nregs = max_reg_num ();
if (!reg_values || reg_values_size < cselib_nregs
|| (reg_values_size > 10 && reg_values_size > cselib_nregs * 4))
{
if (reg_values)
free (reg_values);
reg_values_size = cselib_nregs + (63 + cselib_nregs) / 16;
reg_values = XCNEWVEC (struct elt_list *, reg_values_size);
}
used_regs = XNEWVEC (unsigned int, cselib_nregs);
n_used_regs = 0;
cselib_hash_table = htab_create (31, get_value_hash,
entry_and_rtx_equal_p, NULL);
cselib_current_insn_in_libcall = false;
}
void
cselib_finish (void)
{
free_alloc_pool (elt_list_pool);
free_alloc_pool (elt_loc_list_pool);
free_alloc_pool (cselib_val_pool);
free_alloc_pool (value_pool);
cselib_clear_table ();
htab_delete (cselib_hash_table);
free (used_regs);
used_regs = 0;
cselib_hash_table = 0;
n_useless_values = 0;
next_unknown_value = 0;
}
#include "gt-cselib.h"