#include "config.h"
#include "system.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 "expr.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
#include "hashtab.h"
#include "cselib.h"
static int entry_and_rtx_equal_p PARAMS ((const void *, const void *));
static hashval_t 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 GTY((param_is (cselib_val))) htab_t 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 GTY(()) varray_type reg_values;
static GTY((deletable (""))) varray_type reg_values_old;
#define REG_VALUES(I) VARRAY_ELT_LIST (reg_values, (I))
static unsigned int max_value_regs;
static GTY(()) varray_type used_regs;
static GTY((deletable (""))) varray_type used_regs_old;
static GTY(()) rtx callmem;
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;
static int values_became_useless;
static struct elt_list *
new_elt_list (next, elt)
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 *) ggc_alloc (sizeof (struct elt_list));
el->next = next;
el->elt = elt;
return el;
}
static struct elt_loc_list *
new_elt_loc_list (next, loc)
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 *) 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;
}
static void
unchain_one_elt_list (pl)
struct elt_list **pl;
{
struct elt_list *l = *pl;
*pl = l->next;
l->next = empty_elt_lists;
empty_elt_lists = l;
}
static void
unchain_one_elt_loc_list (pl)
struct elt_loc_list **pl;
{
struct elt_loc_list *l = *pl;
*pl = l->next;
l->next = empty_elt_loc_lists;
empty_elt_loc_lists = l;
}
static void
unchain_one_value (v)
cselib_val *v;
{
while (v->addr_list)
unchain_one_elt_list (&v->addr_list);
v->u.next_free = empty_vals;
empty_vals = v;
}
static void
clear_table (clear_all)
int clear_all;
{
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;
max_value_regs = 0;
VARRAY_POP_ALL (used_regs);
htab_empty (hash_table);
n_useless_values = 0;
next_unknown_value = 0;
}
static int
entry_and_rtx_equal_p (entry, x_arg)
const void *entry, *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);
if (GET_CODE (x) == CONST_INT
|| (mode == VOIDmode && GET_CODE (x) == CONST_DOUBLE))
abort ();
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 (entry)
const void *entry;
{
const cselib_val *v = (const cselib_val *) entry;
return v->value;
}
int
references_value_p (x, only_useless)
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 (x, info)
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 (x, info)
void **x;
void *info ATTRIBUTE_UNUSED;
{
cselib_val *v = (cselib_val *)*x;
if (v->locs == 0)
{
htab_clear_slot (hash_table, x);
unchain_one_value (v);
n_useless_values--;
}
return 1;
}
static void
remove_useless_values ()
{
do
{
values_became_useless = 0;
htab_traverse (hash_table, discard_useless_locs, 0);
}
while (values_became_useless);
htab_traverse (hash_table, discard_useless_values, 0);
if (n_useless_values != 0)
abort ();
}
int
rtx_equal_for_cselib_p (x, y)
rtx x, 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);
if (e)
x = e->u.val_rtx;
}
if (GET_CODE (y) == REG || GET_CODE (y) == MEM)
{
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 (GET_CODE (t) == REG || GET_CODE (t) == MEM)
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 (GET_CODE (t) == REG || GET_CODE (t) == MEM)
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;
if (GET_CODE (x) == LABEL_REF)
return XEXP (x, 0) == XEXP (y, 0);
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 (! 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:
abort ();
}
}
return 1;
}
static rtx
wrap_constant (mode, x)
enum machine_mode mode;
rtx x;
{
if (GET_CODE (x) != CONST_INT
&& (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode))
return x;
if (mode == VOIDmode)
abort ();
return gen_rtx_CONST (mode, x);
}
static unsigned int
hash_rtx (x, mode, create)
rtx x;
enum machine_mode mode;
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) + (unsigned) mode + 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 += hash_rtx (elt, GET_MODE (elt), 0);
}
return hash;
}
case LABEL_REF:
hash
+= ((unsigned) LABEL_REF << 7) + (unsigned long) 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;
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--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
unsigned int tem_hash = hash_rtx (tem, 0, create);
if (tem_hash == 0)
return 0;
hash += tem_hash;
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
{
unsigned int tem_hash = hash_rtx (XVECEXP (x, i, j), 0, create);
if (tem_hash == 0)
return 0;
hash += tem_hash;
}
else if (fmt[i] == 's')
{
const unsigned char *p = (const unsigned char *) XSTR (x, i);
if (p)
while (*p)
hash += *p++;
}
else if (fmt[i] == 'i')
hash += XINT (x, i);
else if (fmt[i] == '0' || fmt[i] == 't')
;
else
abort ();
}
return hash ? hash : 1 + (unsigned int) GET_CODE (x);
}
static cselib_val *
new_cselib_val (value, mode)
unsigned int value;
enum machine_mode mode;
{
cselib_val *e = empty_vals;
if (e)
empty_vals = e->u.next_free;
else
e = (cselib_val *) ggc_alloc (sizeof (cselib_val));
if (value == 0)
abort ();
e->value = value;
e->u.val_rtx = gen_rtx_VALUE (mode);
CSELIB_VAL_PTR (e->u.val_rtx) = e;
e->addr_list = 0;
e->locs = 0;
return e;
}
static void
add_mem_for_addr (addr_elt, mem_elt, x)
cselib_val *addr_elt, *mem_elt;
rtx x;
{
struct elt_loc_list *l;
for (l = mem_elt->locs; l; l = l->next)
if (GET_CODE (l->loc) == MEM
&& 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));
}
static cselib_val *
cselib_lookup_mem (x, create)
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
|| (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 (hash_table, wrap_constant (mode, x),
mem_elt->value, INSERT);
*slot = mem_elt;
return mem_elt;
}
rtx
cselib_subst_to_values (x)
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:
for (l = REG_VALUES (REGNO (x)); l; l = l->next)
if (GET_MODE (l->elt->u.val_rtx) == GET_MODE (x))
return l->elt->u.val_rtx;
abort ();
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 (x, mode, create)
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 (GET_CODE (x) == REG)
{
struct elt_list *l;
unsigned int i = REGNO (x);
for (l = REG_VALUES (i); 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);
slot = htab_find_slot_with_hash (hash_table, x, e->value, INSERT);
*slot = e;
return e;
}
if (GET_CODE (x) == MEM)
return cselib_lookup_mem (x, create);
hashval = hash_rtx (x, mode, create);
if (! hashval)
return 0;
slot = htab_find_slot_with_hash (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 (regno, mode)
unsigned int regno;
enum machine_mode mode;
{
unsigned int endregno;
unsigned int i;
if (reload_completed && regno >= FIRST_PSEUDO_REGISTER
&& reg_renumber[regno] >= 0)
abort ();
if (regno < FIRST_PSEUDO_REGISTER && 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)
this_last += HARD_REGNO_NREGS (i, GET_MODE (v->u.val_rtx)) - 1;
if (this_last < regno)
{
l = &(*l)->next;
continue;
}
unchain_one_elt_list (l);
for (p = &v->locs; ; p = &(*p)->next)
{
rtx x = (*p)->loc;
if (GET_CODE (x) == REG && REGNO (x) == i)
{
unchain_one_elt_loc_list (p);
break;
}
}
if (v->locs == 0)
n_useless_values++;
}
}
}
static int
cselib_mem_conflict_p (mem_base, val)
rtx mem_base;
rtx val;
{
enum rtx_code code;
const char *fmt;
int i, j;
code = GET_CODE (val);
switch (code)
{
case REG:
case PC:
case CC0:
case SCRATCH:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
return 0;
case MEM:
if (GET_MODE (mem_base) == BLKmode
|| GET_MODE (val) == BLKmode
|| anti_dependence (val, mem_base))
return 1;
break;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (cselib_mem_conflict_p (mem_base, XEXP (val, i)))
return 1;
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (val, i); j++)
if (cselib_mem_conflict_p (mem_base, XVECEXP (val, i, j)))
return 1;
}
return 0;
}
static int
cselib_invalidate_mem_1 (slot, info)
void **slot;
void *info;
{
cselib_val *v = (cselib_val *) *slot;
rtx mem_rtx = (rtx) info;
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 (GET_CODE (x) != MEM
|| ! cselib_mem_conflict_p (mem_rtx, x))
{
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++;
return 1;
}
static void
cselib_invalidate_mem (mem_rtx)
rtx mem_rtx;
{
htab_traverse (hash_table, cselib_invalidate_mem_1, mem_rtx);
}
static void
cselib_invalidate_rtx (dest, ignore, data)
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)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG)
cselib_invalidate_regno (REGNO (dest), GET_MODE (dest));
else if (GET_CODE (dest) == MEM)
cselib_invalidate_mem (dest);
if (push_operand (dest, GET_MODE (dest)))
cselib_invalidate_rtx (stack_pointer_rtx, NULL_RTX, NULL);
}
static void
cselib_record_set (dest, src_elt, dest_addr_elt)
rtx dest;
cselib_val *src_elt, *dest_addr_elt;
{
int dreg = GET_CODE (dest) == REG ? (int) REGNO (dest) : -1;
if (src_elt == 0 || side_effects_p (dest))
return;
if (dreg >= 0)
{
if (REG_VALUES (dreg) == 0)
VARRAY_PUSH_UINT (used_regs, dreg);
if (dreg < FIRST_PSEUDO_REGISTER)
{
unsigned int n = HARD_REGNO_NREGS (dreg, GET_MODE (dest));
if (n > max_value_regs)
max_value_regs = n;
}
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);
}
else if (GET_CODE (dest) == MEM && dest_addr_elt != 0)
{
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 (insn)
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 (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
{
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 (GET_CODE (dest) == MEM)
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, NULL);
for (i = 0; i < n_sets; i++)
{
rtx dest = sets[i].dest;
if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
cselib_record_set (dest, sets[i].src_elt, sets[i].dest_addr_elt);
}
}
void
cselib_process_insn (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;
if (GET_CODE (insn) == CODE_LABEL
|| (GET_CODE (insn) == CALL_INSN
&& find_reg_note (insn, REG_SETJMP, NULL))
|| (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == ASM_OPERANDS
&& MEM_VOLATILE_P (PATTERN (insn))))
{
clear_table (0);
return;
}
if (! INSN_P (insn))
{
cselib_current_insn = 0;
return;
}
if (GET_CODE (insn) == CALL_INSN)
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i])
cselib_invalidate_regno (i, VOIDmode);
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), NULL_RTX, NULL);
#endif
if (GET_CODE (insn) == CALL_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), NULL_RTX, NULL);
cselib_current_insn = 0;
if (n_useless_values > MAX_USELESS_VALUES)
remove_useless_values ();
}
void
cselib_update_varray_sizes ()
{
unsigned int nregs = max_reg_num ();
if (nregs == cselib_nregs)
return;
cselib_nregs = nregs;
VARRAY_GROW (reg_values, nregs);
VARRAY_GROW (used_regs, nregs);
}
void
cselib_init ()
{
if (! callmem)
callmem = gen_rtx_MEM (BLKmode, const0_rtx);
cselib_nregs = max_reg_num ();
if (reg_values_old != NULL && VARRAY_SIZE (reg_values_old) >= cselib_nregs)
{
reg_values = reg_values_old;
used_regs = used_regs_old;
VARRAY_CLEAR (reg_values);
VARRAY_CLEAR (used_regs);
}
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);
clear_table (1);
cselib_current_insn_in_libcall = false;
}
void
cselib_finish ()
{
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"