#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "regs.h"
#include "basic-block.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "function.h"
#include "expr.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
#include "timevar.h"
#include "except.h"
#include "target.h"
#include "params.h"
#include "rtlhooks-def.h"
static int max_qty;
static int next_qty;
struct qty_table_elem
{
rtx const_rtx;
rtx const_insn;
rtx comparison_const;
int comparison_qty;
unsigned int first_reg, last_reg;
ENUM_BITFIELD(rtx_code) comparison_code : 16;
ENUM_BITFIELD(machine_mode) mode : 8;
};
static struct qty_table_elem *qty_table;
struct change_cc_mode_args
{
rtx insn;
rtx newreg;
};
#ifdef HAVE_cc0
static rtx prev_insn_cc0;
static enum machine_mode prev_insn_cc0_mode;
static rtx prev_insn;
#endif
static rtx this_insn;
struct reg_eqv_elem
{
int next, prev;
};
static struct reg_eqv_elem *reg_eqv_table;
struct cse_reg_info
{
unsigned int timestamp;
int reg_qty;
int reg_tick;
int reg_in_table;
unsigned int subreg_ticked;
};
struct cse_reg_info *cse_reg_info_table;
static unsigned int cse_reg_info_table_size;
static unsigned int cse_reg_info_table_first_uninitialized;
static unsigned int cse_reg_info_timestamp;
static HARD_REG_SET hard_regs_in_table;
static int cse_basic_block_start;
static int cse_basic_block_end;
static int *uid_cuid;
static int max_uid;
#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
static int cse_altered;
static int cse_jumps_altered;
static int recorded_label_ref;
static int do_not_record;
static int hash_arg_in_memory;
struct table_elt
{
rtx exp;
rtx canon_exp;
struct table_elt *next_same_hash;
struct table_elt *prev_same_hash;
struct table_elt *next_same_value;
struct table_elt *prev_same_value;
struct table_elt *first_same_value;
struct table_elt *related_value;
int cost;
int regcost;
ENUM_BITFIELD(machine_mode) mode : 8;
char in_memory;
char is_const;
char flag;
};
#define HASH_SHIFT 5
#define HASH_SIZE (1 << HASH_SHIFT)
#define HASH_MASK (HASH_SIZE - 1)
#define HASH(X, M) \
((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER \
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \
: canon_hash (X, M)) & HASH_MASK)
#define SAFE_HASH(X, M) \
((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER \
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \
: safe_hash (X, M)) & HASH_MASK)
#define FIXED_REGNO_P(N) \
((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| fixed_regs[N] || global_regs[N])
#define CHEAP_REGNO(N) \
(REGNO_PTR_FRAME_P(N) \
|| (HARD_REGISTER_NUM_P (N) \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
#define COST(X) (REG_P (X) ? 0 : notreg_cost (X, SET))
#define COST_IN(X,OUTER) (REG_P (X) ? 0 : notreg_cost (X, OUTER))
#define REG_TICK(N) (get_cse_reg_info (N)->reg_tick)
#define REG_IN_TABLE(N) (get_cse_reg_info (N)->reg_in_table)
#define SUBREG_TICKED(N) (get_cse_reg_info (N)->subreg_ticked)
#define REG_QTY(N) (get_cse_reg_info (N)->reg_qty)
#define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0)
static struct table_elt *table[HASH_SIZE];
static struct table_elt *free_element_chain;
static int constant_pool_entries_cost;
static int constant_pool_entries_regcost;
struct cse_basic_block_data
{
int low_cuid;
int high_cuid;
int nsets;
rtx last;
int path_size;
struct branch_path
{
rtx branch;
enum taken {PATH_TAKEN, PATH_NOT_TAKEN, PATH_AROUND} status;
} *path;
};
static bool fixed_base_plus_p (rtx x);
static int notreg_cost (rtx, enum rtx_code);
static int approx_reg_cost_1 (rtx *, void *);
static int approx_reg_cost (rtx);
static int preferable (int, int, int, int);
static void new_basic_block (void);
static void make_new_qty (unsigned int, enum machine_mode);
static void make_regs_eqv (unsigned int, unsigned int);
static void delete_reg_equiv (unsigned int);
static int mention_regs (rtx);
static int insert_regs (rtx, struct table_elt *, int);
static void remove_from_table (struct table_elt *, unsigned);
static struct table_elt *lookup (rtx, unsigned, enum machine_mode);
static struct table_elt *lookup_for_remove (rtx, unsigned, enum machine_mode);
static rtx lookup_as_function (rtx, enum rtx_code);
static struct table_elt *insert (rtx, struct table_elt *, unsigned,
enum machine_mode);
static void merge_equiv_classes (struct table_elt *, struct table_elt *);
static void invalidate (rtx, enum machine_mode);
static int cse_rtx_varies_p (rtx, int);
static void remove_invalid_refs (unsigned int);
static void remove_invalid_subreg_refs (unsigned int, unsigned int,
enum machine_mode);
static void rehash_using_reg (rtx);
static void invalidate_memory (void);
static void invalidate_for_call (void);
static rtx use_related_value (rtx, struct table_elt *);
static inline unsigned canon_hash (rtx, enum machine_mode);
static inline unsigned safe_hash (rtx, enum machine_mode);
static unsigned hash_rtx_string (const char *);
static rtx canon_reg (rtx, rtx);
static void find_best_addr (rtx, rtx *, enum machine_mode);
static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *,
enum machine_mode *,
enum machine_mode *);
static rtx fold_rtx (rtx, rtx);
static rtx equiv_constant (rtx);
static void record_jump_equiv (rtx, int);
static void record_jump_cond (enum rtx_code, enum machine_mode, rtx, rtx,
int);
static void cse_insn (rtx, rtx);
static void cse_end_of_basic_block (rtx, struct cse_basic_block_data *,
int, int);
static int addr_affects_sp_p (rtx);
static void invalidate_from_clobbers (rtx);
static rtx cse_process_notes (rtx, rtx);
static void invalidate_skipped_set (rtx, rtx, void *);
static void invalidate_skipped_block (rtx);
static rtx cse_basic_block (rtx, rtx, struct branch_path *);
static void count_reg_usage (rtx, int *, int);
static int check_for_label_ref (rtx *, void *);
extern void dump_class (struct table_elt*);
static void get_cse_reg_info_1 (unsigned int regno);
static struct cse_reg_info * get_cse_reg_info (unsigned int regno);
static int check_dependence (rtx *, void *);
static void flush_hash_table (void);
static bool insn_live_p (rtx, int *);
static bool set_live_p (rtx, rtx, int *);
static bool dead_libcall_p (rtx, int *);
static int cse_change_cc_mode (rtx *, void *);
static void cse_change_cc_mode_insn (rtx, rtx);
static void cse_change_cc_mode_insns (rtx, rtx, rtx);
static enum machine_mode cse_cc_succs (basic_block, rtx, rtx, bool);
#undef RTL_HOOKS_GEN_LOWPART
#define RTL_HOOKS_GEN_LOWPART gen_lowpart_if_possible
static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER;
static bool
fixed_base_plus_p (rtx x)
{
switch (GET_CODE (x))
{
case REG:
if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx)
return true;
if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])
return true;
if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
&& REGNO (x) <= LAST_VIRTUAL_REGISTER)
return true;
return false;
case PLUS:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
return false;
return fixed_base_plus_p (XEXP (x, 0));
default:
return false;
}
}
void
dump_class (struct table_elt *classp)
{
struct table_elt *elt;
fprintf (stderr, "Equivalence chain for ");
print_rtl (stderr, classp->exp);
fprintf (stderr, ": \n");
for (elt = classp->first_same_value; elt; elt = elt->next_same_value)
{
print_rtl (stderr, elt->exp);
fprintf (stderr, "\n");
}
}
static int
approx_reg_cost_1 (rtx *xp, void *data)
{
rtx x = *xp;
int *cost_p = data;
if (x && REG_P (x))
{
unsigned int regno = REGNO (x);
if (! CHEAP_REGNO (regno))
{
if (regno < FIRST_PSEUDO_REGISTER)
{
if (SMALL_REGISTER_CLASSES)
return 1;
*cost_p += 2;
}
else
*cost_p += 1;
}
}
return 0;
}
static int
approx_reg_cost (rtx x)
{
int cost = 0;
if (for_each_rtx (&x, approx_reg_cost_1, (void *) &cost))
return MAX_COST;
return cost;
}
static rtx
canon_for_address (rtx x)
{
enum rtx_code code;
enum machine_mode mode;
rtx new = 0;
int i;
const char *fmt;
if (!x)
return x;
code = GET_CODE (x);
mode = GET_MODE (x);
switch (code)
{
case ASHIFT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode)
&& INTVAL (XEXP (x, 1)) >= 0)
{
new = canon_for_address (XEXP (x, 0));
new = gen_rtx_MULT (mode, new,
gen_int_mode ((HOST_WIDE_INT) 1
<< INTVAL (XEXP (x, 1)),
mode));
}
break;
default:
break;
}
if (new)
return new;
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < GET_RTX_LENGTH (code); i++)
if (fmt[i] == 'e')
{
new = canon_for_address (XEXP (x, i));
XEXP (x, i) = new;
}
return x;
}
static int
preferable (int cost_a, int regcost_a, int cost_b, int regcost_b)
{
if (cost_a != cost_b)
{
if (cost_a == MAX_COST)
return 1;
if (cost_b == MAX_COST)
return -1;
}
if (regcost_a != regcost_b)
{
if (regcost_a == MAX_COST)
return 1;
if (regcost_b == MAX_COST)
return -1;
}
if (cost_a != cost_b)
return cost_a - cost_b;
if (regcost_a != regcost_b)
return regcost_a - regcost_b;
return 0;
}
static int
notreg_cost (rtx x, enum rtx_code outer)
{
return ((GET_CODE (x) == SUBREG
&& REG_P (SUBREG_REG (x))
&& GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
&& GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
&& (GET_MODE_SIZE (GET_MODE (x))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
&& subreg_lowpart_p (x)
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (x)),
GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))))
? 0
: rtx_cost (x, outer) * 2);
}
static void
init_cse_reg_info (unsigned int nregs)
{
if (nregs > cse_reg_info_table_size)
{
unsigned int new_size;
if (cse_reg_info_table_size < 2048)
{
new_size = (cse_reg_info_table_size
? cse_reg_info_table_size : 64);
while (new_size < nregs)
new_size *= 2;
}
else
{
new_size = nregs;
}
if (cse_reg_info_table)
free (cse_reg_info_table);
cse_reg_info_table = xmalloc (sizeof (struct cse_reg_info)
* new_size);
cse_reg_info_table_size = new_size;
cse_reg_info_table_first_uninitialized = 0;
}
if (cse_reg_info_table_first_uninitialized < nregs)
{
unsigned int old_timestamp = cse_reg_info_timestamp - 1;
unsigned int i;
for (i = cse_reg_info_table_first_uninitialized; i < nregs; i++)
cse_reg_info_table[i].timestamp = old_timestamp;
cse_reg_info_table_first_uninitialized = nregs;
}
}
static void
get_cse_reg_info_1 (unsigned int regno)
{
cse_reg_info_table[regno].timestamp = cse_reg_info_timestamp;
cse_reg_info_table[regno].reg_tick = 1;
cse_reg_info_table[regno].reg_in_table = -1;
cse_reg_info_table[regno].subreg_ticked = -1;
cse_reg_info_table[regno].reg_qty = -regno - 1;
}
static inline struct cse_reg_info *
get_cse_reg_info (unsigned int regno)
{
struct cse_reg_info *p = &cse_reg_info_table[regno];
if (p->timestamp != cse_reg_info_timestamp)
get_cse_reg_info_1 (regno);
return p;
}
static void
new_basic_block (void)
{
int i;
next_qty = 0;
cse_reg_info_timestamp++;
CLEAR_HARD_REG_SET (hard_regs_in_table);
for (i = 0; i < HASH_SIZE; i++)
{
struct table_elt *first;
first = table[i];
if (first != NULL)
{
struct table_elt *last = first;
table[i] = NULL;
while (last->next_same_hash != NULL)
last = last->next_same_hash;
last->next_same_hash = free_element_chain;
free_element_chain = first;
}
}
#ifdef HAVE_cc0
prev_insn = 0;
prev_insn_cc0 = 0;
#endif
}
static void
make_new_qty (unsigned int reg, enum machine_mode mode)
{
int q;
struct qty_table_elem *ent;
struct reg_eqv_elem *eqv;
gcc_assert (next_qty < max_qty);
q = REG_QTY (reg) = next_qty++;
ent = &qty_table[q];
ent->first_reg = reg;
ent->last_reg = reg;
ent->mode = mode;
ent->const_rtx = ent->const_insn = NULL_RTX;
ent->comparison_code = UNKNOWN;
eqv = ®_eqv_table[reg];
eqv->next = eqv->prev = -1;
}
static void
make_regs_eqv (unsigned int new, unsigned int old)
{
unsigned int lastr, firstr;
int q = REG_QTY (old);
struct qty_table_elem *ent;
ent = &qty_table[q];
gcc_assert (REGNO_QTY_VALID_P (old));
REG_QTY (new) = q;
firstr = ent->first_reg;
lastr = ent->last_reg;
if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr))
&& (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS)
&& ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new))
|| (new >= FIRST_PSEUDO_REGISTER
&& (firstr < FIRST_PSEUDO_REGISTER
|| ((uid_cuid[REGNO_LAST_UID (new)] > cse_basic_block_end
|| (uid_cuid[REGNO_FIRST_UID (new)]
< cse_basic_block_start))
&& (uid_cuid[REGNO_LAST_UID (new)]
> uid_cuid[REGNO_LAST_UID (firstr)]))))))
{
reg_eqv_table[firstr].prev = new;
reg_eqv_table[new].next = firstr;
reg_eqv_table[new].prev = -1;
ent->first_reg = new;
}
else
{
while (lastr < FIRST_PSEUDO_REGISTER && reg_eqv_table[lastr].prev >= 0
&& (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr))
&& new >= FIRST_PSEUDO_REGISTER)
lastr = reg_eqv_table[lastr].prev;
reg_eqv_table[new].next = reg_eqv_table[lastr].next;
if (reg_eqv_table[lastr].next >= 0)
reg_eqv_table[reg_eqv_table[lastr].next].prev = new;
else
qty_table[q].last_reg = new;
reg_eqv_table[lastr].next = new;
reg_eqv_table[new].prev = lastr;
}
}
static void
delete_reg_equiv (unsigned int reg)
{
struct qty_table_elem *ent;
int q = REG_QTY (reg);
int p, n;
if (! REGNO_QTY_VALID_P (reg))
return;
ent = &qty_table[q];
p = reg_eqv_table[reg].prev;
n = reg_eqv_table[reg].next;
if (n != -1)
reg_eqv_table[n].prev = p;
else
ent->last_reg = p;
if (p != -1)
reg_eqv_table[p].next = n;
else
ent->first_reg = n;
REG_QTY (reg) = -reg - 1;
}
static int
mention_regs (rtx x)
{
enum rtx_code code;
int i, j;
const char *fmt;
int changed = 0;
if (x == 0)
return 0;
code = GET_CODE (x);
if (code == REG)
{
unsigned int regno = REGNO (x);
unsigned int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: hard_regno_nregs[regno][GET_MODE (x)]);
unsigned int i;
for (i = regno; i < endregno; i++)
{
if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
remove_invalid_refs (i);
REG_IN_TABLE (i) = REG_TICK (i);
SUBREG_TICKED (i) = -1;
}
return 0;
}
if (code == SUBREG && REG_P (SUBREG_REG (x))
&& REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
{
unsigned int i = REGNO (SUBREG_REG (x));
if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
{
if (REG_TICK (i) - REG_IN_TABLE (i) > 1
|| SUBREG_TICKED (i) != REGNO (SUBREG_REG (x)))
remove_invalid_refs (i);
else
remove_invalid_subreg_refs (i, SUBREG_BYTE (x), GET_MODE (x));
}
REG_IN_TABLE (i) = REG_TICK (i);
SUBREG_TICKED (i) = REGNO (SUBREG_REG (x));
return 0;
}
if (code == COMPARE || COMPARISON_P (x))
{
if (REG_P (XEXP (x, 0))
&& ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
if (insert_regs (XEXP (x, 0), NULL, 0))
{
rehash_using_reg (XEXP (x, 0));
changed = 1;
}
if (REG_P (XEXP (x, 1))
&& ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
if (insert_regs (XEXP (x, 1), NULL, 0))
{
rehash_using_reg (XEXP (x, 1));
changed = 1;
}
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
changed |= mention_regs (XEXP (x, i));
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
changed |= mention_regs (XVECEXP (x, i, j));
return changed;
}
static int
insert_regs (rtx x, struct table_elt *classp, int modified)
{
if (REG_P (x))
{
unsigned int regno = REGNO (x);
int qty_valid;
qty_valid = REGNO_QTY_VALID_P (regno);
if (qty_valid)
{
struct qty_table_elem *ent = &qty_table[REG_QTY (regno)];
if (ent->mode != GET_MODE (x))
return 0;
}
if (modified || ! qty_valid)
{
if (classp)
for (classp = classp->first_same_value;
classp != 0;
classp = classp->next_same_value)
if (REG_P (classp->exp)
&& GET_MODE (classp->exp) == GET_MODE (x))
{
unsigned c_regno = REGNO (classp->exp);
gcc_assert (REGNO_QTY_VALID_P (c_regno));
if (qty_table[REG_QTY (c_regno)].mode != GET_MODE (x))
continue;
make_regs_eqv (regno, c_regno);
return 1;
}
if (! modified
&& REG_IN_TABLE (regno) >= 0
&& REG_TICK (regno) == REG_IN_TABLE (regno) + 1)
REG_TICK (regno)++;
make_new_qty (regno, GET_MODE (x));
return 1;
}
return 0;
}
else if (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x))
&& ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x))))
{
insert_regs (SUBREG_REG (x), NULL, 0);
mention_regs (x);
return 1;
}
else
return mention_regs (x);
}
static void
remove_from_table (struct table_elt *elt, unsigned int hash)
{
if (elt == 0)
return;
elt->first_same_value = 0;
{
struct table_elt *prev = elt->prev_same_value;
struct table_elt *next = elt->next_same_value;
if (next)
next->prev_same_value = prev;
if (prev)
prev->next_same_value = next;
else
{
struct table_elt *newfirst = next;
while (next)
{
next->first_same_value = newfirst;
next = next->next_same_value;
}
}
}
{
struct table_elt *prev = elt->prev_same_hash;
struct table_elt *next = elt->next_same_hash;
if (next)
next->prev_same_hash = prev;
if (prev)
prev->next_same_hash = next;
else if (table[hash] == elt)
table[hash] = next;
else
{
for (hash = 0; hash < HASH_SIZE; hash++)
if (table[hash] == elt)
table[hash] = next;
}
}
if (elt->related_value != 0 && elt->related_value != elt)
{
struct table_elt *p = elt->related_value;
while (p->related_value != elt)
p = p->related_value;
p->related_value = elt->related_value;
if (p->related_value == p)
p->related_value = 0;
}
elt->next_same_hash = free_element_chain;
free_element_chain = elt;
}
static struct table_elt *
lookup (rtx x, unsigned int hash, enum machine_mode mode)
{
struct table_elt *p;
for (p = table[hash]; p; p = p->next_same_hash)
if (mode == p->mode && ((x == p->exp && REG_P (x))
|| exp_equiv_p (x, p->exp, !REG_P (x), false)))
return p;
return 0;
}
static struct table_elt *
lookup_for_remove (rtx x, unsigned int hash, enum machine_mode mode)
{
struct table_elt *p;
if (REG_P (x))
{
unsigned int regno = REGNO (x);
for (p = table[hash]; p; p = p->next_same_hash)
if (REG_P (p->exp)
&& REGNO (p->exp) == regno)
return p;
}
else
{
for (p = table[hash]; p; p = p->next_same_hash)
if (mode == p->mode
&& (x == p->exp || exp_equiv_p (x, p->exp, 0, false)))
return p;
}
return 0;
}
static rtx
lookup_as_function (rtx x, enum rtx_code code)
{
struct table_elt *p
= lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x));
if (p == 0 && code == CONST_INT
&& GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode))
{
x = copy_rtx (x);
PUT_MODE (x, word_mode);
p = lookup (x, SAFE_HASH (x, VOIDmode), word_mode);
}
if (p == 0)
return 0;
for (p = p->first_same_value; p; p = p->next_same_value)
if (GET_CODE (p->exp) == code
&& exp_equiv_p (p->exp, p->exp, 1, false))
return p->exp;
return 0;
}
#define CHEAPER(X, Y) \
(preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
static struct table_elt *
insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode)
{
struct table_elt *elt;
gcc_assert (!REG_P (x) || REGNO_QTY_VALID_P (REGNO (x)));
if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
{
unsigned int regno = REGNO (x);
unsigned int endregno = regno + hard_regno_nregs[regno][GET_MODE (x)];
unsigned int i;
for (i = regno; i < endregno; i++)
SET_HARD_REG_BIT (hard_regs_in_table, i);
}
elt = free_element_chain;
if (elt)
free_element_chain = elt->next_same_hash;
else
elt = xmalloc (sizeof (struct table_elt));
elt->exp = x;
elt->canon_exp = NULL_RTX;
elt->cost = COST (x);
elt->regcost = approx_reg_cost (x);
elt->next_same_value = 0;
elt->prev_same_value = 0;
elt->next_same_hash = table[hash];
elt->prev_same_hash = 0;
elt->related_value = 0;
elt->in_memory = 0;
elt->mode = mode;
elt->is_const = (CONSTANT_P (x) || fixed_base_plus_p (x));
if (table[hash])
table[hash]->prev_same_hash = elt;
table[hash] = elt;
if (classp)
{
classp = classp->first_same_value;
if (CHEAPER (elt, classp))
{
struct table_elt *p;
elt->next_same_value = classp;
classp->prev_same_value = elt;
elt->first_same_value = elt;
for (p = classp; p; p = p->next_same_value)
p->first_same_value = elt;
}
else
{
struct table_elt *p, *next;
for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt);
p = next);
elt->next_same_value = next;
if (next)
next->prev_same_value = elt;
elt->prev_same_value = p;
p->next_same_value = elt;
elt->first_same_value = classp;
}
}
else
elt->first_same_value = elt;
if (elt->is_const && classp && REG_P (classp->exp)
&& !REG_P (x))
{
int exp_q = REG_QTY (REGNO (classp->exp));
struct qty_table_elem *exp_ent = &qty_table[exp_q];
exp_ent->const_rtx = gen_lowpart (exp_ent->mode, x);
exp_ent->const_insn = this_insn;
}
else if (REG_P (x)
&& classp
&& ! qty_table[REG_QTY (REGNO (x))].const_rtx
&& ! elt->is_const)
{
struct table_elt *p;
for (p = classp; p != 0; p = p->next_same_value)
{
if (p->is_const && !REG_P (p->exp))
{
int x_q = REG_QTY (REGNO (x));
struct qty_table_elem *x_ent = &qty_table[x_q];
x_ent->const_rtx
= gen_lowpart (GET_MODE (x), p->exp);
x_ent->const_insn = this_insn;
break;
}
}
}
else if (REG_P (x)
&& qty_table[REG_QTY (REGNO (x))].const_rtx
&& GET_MODE (x) == qty_table[REG_QTY (REGNO (x))].mode)
qty_table[REG_QTY (REGNO (x))].const_insn = this_insn;
if (GET_CODE (x) == CONST)
{
rtx subexp = get_related_value (x);
unsigned subhash;
struct table_elt *subelt, *subelt_prev;
if (subexp != 0)
{
subhash = SAFE_HASH (subexp, mode);
subelt = lookup (subexp, subhash, mode);
if (subelt == 0)
subelt = insert (subexp, NULL, subhash, mode);
if (subelt->related_value == 0)
subelt->related_value = subelt;
subelt_prev = subelt;
while (subelt_prev->related_value != subelt)
subelt_prev = subelt_prev->related_value;
elt->related_value = subelt_prev->related_value;
subelt_prev->related_value = elt;
}
}
return elt;
}
static void
merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
{
struct table_elt *elt, *next, *new;
class1 = class1->first_same_value;
class2 = class2->first_same_value;
if (class1 == class2)
return;
for (elt = class2; elt; elt = next)
{
unsigned int hash;
rtx exp = elt->exp;
enum machine_mode mode = elt->mode;
next = elt->next_same_value;
if (REG_P (exp) || exp_equiv_p (exp, exp, 1, false))
{
bool need_rehash = false;
hash_arg_in_memory = 0;
hash = HASH (exp, mode);
if (REG_P (exp))
{
need_rehash = REGNO_QTY_VALID_P (REGNO (exp));
delete_reg_equiv (REGNO (exp));
}
remove_from_table (elt, hash);
if (insert_regs (exp, class1, 0) || need_rehash)
{
rehash_using_reg (exp);
hash = HASH (exp, mode);
}
new = insert (exp, class1, hash, mode);
new->in_memory = hash_arg_in_memory;
}
}
}
static void
flush_hash_table (void)
{
int i;
struct table_elt *p;
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = table[i])
{
if (REG_P (p->exp))
invalidate (p->exp, p->mode);
else
remove_from_table (p, i);
}
}
struct check_dependence_data
{
enum machine_mode mode;
rtx exp;
rtx addr;
};
static int
check_dependence (rtx *x, void *data)
{
struct check_dependence_data *d = (struct check_dependence_data *) data;
if (*x && MEM_P (*x))
return canon_true_dependence (d->exp, d->mode, d->addr, *x,
cse_rtx_varies_p);
else
return 0;
}
static void
invalidate (rtx x, enum machine_mode full_mode)
{
int i;
struct table_elt *p;
rtx addr;
switch (GET_CODE (x))
{
case REG:
{
unsigned int regno = REGNO (x);
unsigned int hash = HASH (x, GET_MODE (x));
delete_reg_equiv (regno);
REG_TICK (regno)++;
SUBREG_TICKED (regno) = -1;
if (regno >= FIRST_PSEUDO_REGISTER)
{
struct table_elt *elt;
while ((elt = lookup_for_remove (x, hash, GET_MODE (x))))
remove_from_table (elt, hash);
}
else
{
HOST_WIDE_INT in_table
= TEST_HARD_REG_BIT (hard_regs_in_table, regno);
unsigned int endregno
= regno + hard_regno_nregs[regno][GET_MODE (x)];
unsigned int tregno, tendregno, rn;
struct table_elt *p, *next;
CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
for (rn = regno + 1; rn < endregno; rn++)
{
in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
delete_reg_equiv (rn);
REG_TICK (rn)++;
SUBREG_TICKED (rn) = -1;
}
if (in_table)
for (hash = 0; hash < HASH_SIZE; hash++)
for (p = table[hash]; p; p = next)
{
next = p->next_same_hash;
if (!REG_P (p->exp)
|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
continue;
tregno = REGNO (p->exp);
tendregno
= tregno + hard_regno_nregs[tregno][GET_MODE (p->exp)];
if (tendregno > regno && tregno < endregno)
remove_from_table (p, hash);
}
}
}
return;
case SUBREG:
invalidate (SUBREG_REG (x), VOIDmode);
return;
case PARALLEL:
for (i = XVECLEN (x, 0) - 1; i >= 0; --i)
invalidate (XVECEXP (x, 0, i), VOIDmode);
return;
case EXPR_LIST:
invalidate (XEXP (x, 0), VOIDmode);
return;
case MEM:
addr = canon_rtx (get_addr (XEXP (x, 0)));
x = canon_rtx (x);
if (full_mode == VOIDmode)
full_mode = GET_MODE (x);
for (i = 0; i < HASH_SIZE; i++)
{
struct table_elt *next;
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory)
{
struct check_dependence_data d;
if (!p->canon_exp)
p->canon_exp = canon_rtx (p->exp);
d.exp = x;
d.addr = addr;
d.mode = full_mode;
if (for_each_rtx (&p->canon_exp, check_dependence, &d))
remove_from_table (p, i);
}
}
}
return;
default:
gcc_unreachable ();
}
}
static void
remove_invalid_refs (unsigned int regno)
{
unsigned int i;
struct table_elt *p, *next;
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (!REG_P (p->exp)
&& refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
remove_from_table (p, i);
}
}
static void
remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
enum machine_mode mode)
{
unsigned int i;
struct table_elt *p, *next;
unsigned int end = offset + (GET_MODE_SIZE (mode) - 1);
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = next)
{
rtx exp = p->exp;
next = p->next_same_hash;
if (!REG_P (exp)
&& (GET_CODE (exp) != SUBREG
|| !REG_P (SUBREG_REG (exp))
|| REGNO (SUBREG_REG (exp)) != regno
|| (((SUBREG_BYTE (exp)
+ (GET_MODE_SIZE (GET_MODE (exp)) - 1)) >= offset)
&& SUBREG_BYTE (exp) <= end))
&& refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
remove_from_table (p, i);
}
}
static void
rehash_using_reg (rtx x)
{
unsigned int i;
struct table_elt *p, *next;
unsigned hash;
if (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
if (!REG_P (x)
|| REG_IN_TABLE (REGNO (x)) < 0
|| REG_IN_TABLE (REGNO (x)) != REG_TICK (REGNO (x)))
return;
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (reg_mentioned_p (x, p->exp)
&& exp_equiv_p (p->exp, p->exp, 1, false)
&& i != (hash = SAFE_HASH (p->exp, p->mode)))
{
if (p->next_same_hash)
p->next_same_hash->prev_same_hash = p->prev_same_hash;
if (p->prev_same_hash)
p->prev_same_hash->next_same_hash = p->next_same_hash;
else
table[i] = p->next_same_hash;
p->next_same_hash = table[hash];
p->prev_same_hash = 0;
if (table[hash])
table[hash]->prev_same_hash = p;
table[hash] = p;
}
}
}
static void
invalidate_for_call (void)
{
unsigned int regno, endregno;
unsigned int i;
unsigned hash;
struct table_elt *p, *next;
int in_table = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
{
delete_reg_equiv (regno);
if (REG_TICK (regno) >= 0)
{
REG_TICK (regno)++;
SUBREG_TICKED (regno) = -1;
}
in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
}
if (in_table)
for (hash = 0; hash < HASH_SIZE; hash++)
for (p = table[hash]; p; p = next)
{
next = p->next_same_hash;
if (!REG_P (p->exp)
|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
continue;
regno = REGNO (p->exp);
endregno = regno + hard_regno_nregs[regno][GET_MODE (p->exp)];
for (i = regno; i < endregno; i++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
{
remove_from_table (p, hash);
break;
}
}
}
static rtx
use_related_value (rtx x, struct table_elt *elt)
{
struct table_elt *relt = 0;
struct table_elt *p, *q;
HOST_WIDE_INT offset;
if (elt != 0 && elt->related_value != 0)
relt = elt;
else if (elt == 0 && GET_CODE (x) == CONST)
{
rtx subexp = get_related_value (x);
if (subexp != 0)
relt = lookup (subexp,
SAFE_HASH (subexp, GET_MODE (subexp)),
GET_MODE (subexp));
}
if (relt == 0)
return 0;
p = relt;
while (1)
{
if (rtx_equal_p (x, p->exp))
q = 0;
else
for (q = p->first_same_value; q; q = q->next_same_value)
if (REG_P (q->exp))
break;
if (q)
break;
p = p->related_value;
if (p == relt || p == 0)
break;
}
if (q == 0)
return 0;
offset = (get_integer_term (x) - get_integer_term (p->exp));
return plus_constant (q->exp, offset);
}
static inline unsigned
hash_rtx_string (const char *ps)
{
unsigned hash = 0;
const unsigned char *p = (const unsigned char *) ps;
if (p)
while (*p)
hash += *p++;
return hash;
}
unsigned
hash_rtx (rtx x, enum machine_mode mode, int *do_not_record_p,
int *hash_arg_in_memory_p, bool have_reg_qty)
{
int i, j;
unsigned hash = 0;
enum rtx_code code;
const char *fmt;
repeat:
if (x == 0)
return hash;
code = GET_CODE (x);
switch (code)
{
case REG:
{
unsigned int regno = REGNO (x);
if (!reload_completed)
{
bool record;
if (regno >= FIRST_PSEUDO_REGISTER)
record = true;
else if (x == frame_pointer_rtx
|| x == hard_frame_pointer_rtx
|| x == arg_pointer_rtx
|| x == stack_pointer_rtx
|| x == pic_offset_table_rtx)
record = true;
else if (global_regs[regno])
record = false;
else if (fixed_regs[regno])
record = true;
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
record = true;
else if (SMALL_REGISTER_CLASSES)
record = false;
else if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno)))
record = false;
else
record = true;
if (!record)
{
*do_not_record_p = 1;
return 0;
}
}
hash += ((unsigned int) REG << 7);
hash += (have_reg_qty ? (unsigned) REG_QTY (regno) : regno);
return hash;
}
case SUBREG:
{
if (REG_P (SUBREG_REG (x)))
{
hash += (((unsigned int) SUBREG << 7)
+ REGNO (SUBREG_REG (x))
+ (SUBREG_BYTE (x) / UNITS_PER_WORD));
return hash;
}
break;
}
case CONST_INT:
hash += (((unsigned int) CONST_INT << 7) + (unsigned int) mode
+ (unsigned int) INTVAL (x));
return hash;
case CONST_DOUBLE:
hash += (unsigned int) code + (unsigned int) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
else
hash += ((unsigned int) CONST_DOUBLE_LOW (x)
+ (unsigned int) CONST_DOUBLE_HIGH (x));
return hash;
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), do_not_record_p,
hash_arg_in_memory_p, have_reg_qty);
}
return hash;
}
case LABEL_REF:
hash += (((unsigned int) LABEL_REF << 7)
+ CODE_LABEL_NUMBER (XEXP (x, 0)));
return hash;
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;
}
case MEM:
if (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)
{
*do_not_record_p = 1;
return 0;
}
if (hash_arg_in_memory_p && !MEM_READONLY_P (x))
*hash_arg_in_memory_p = 1;
hash += (unsigned) MEM;
x = XEXP (x, 0);
goto repeat;
case USE:
if (MEM_P (XEXP (x, 0))
&& ! MEM_VOLATILE_P (XEXP (x, 0)))
{
hash += (unsigned) USE;
x = XEXP (x, 0);
if (hash_arg_in_memory_p && !MEM_READONLY_P (x))
*hash_arg_in_memory_p = 1;
hash += (unsigned) MEM;
x = XEXP (x, 0);
goto repeat;
}
break;
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
case PRE_MODIFY:
case POST_MODIFY:
case PC:
case CC0:
case CALL:
case UNSPEC_VOLATILE:
*do_not_record_p = 1;
return 0;
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
{
*do_not_record_p = 1;
return 0;
}
else
{
hash += (unsigned) code + (unsigned) GET_MODE (x)
+ hash_rtx_string (ASM_OPERANDS_TEMPLATE (x))
+ hash_rtx_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (x))
+ (unsigned) ASM_OPERANDS_OUTPUT_IDX (x);
if (ASM_OPERANDS_INPUT_LENGTH (x))
{
for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
{
hash += (hash_rtx (ASM_OPERANDS_INPUT (x, i),
GET_MODE (ASM_OPERANDS_INPUT (x, i)),
do_not_record_p, hash_arg_in_memory_p,
have_reg_qty)
+ hash_rtx_string
(ASM_OPERANDS_INPUT_CONSTRAINT (x, i)));
}
hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
x = ASM_OPERANDS_INPUT (x, 0);
mode = GET_MODE (x);
goto repeat;
}
return hash;
}
break;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
hash += (unsigned) code + (unsigned) GET_MODE (x);
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
switch (fmt[i])
{
case 'e':
if (i == 0)
{
x = XEXP (x, i);
goto repeat;
}
hash += hash_rtx (XEXP (x, i), 0, do_not_record_p,
hash_arg_in_memory_p, have_reg_qty);
break;
case 'E':
for (j = 0; j < XVECLEN (x, i); j++)
hash += hash_rtx (XVECEXP (x, i, j), 0, do_not_record_p,
hash_arg_in_memory_p, have_reg_qty);
break;
case 's':
hash += hash_rtx_string (XSTR (x, i));
break;
case 'i':
hash += (unsigned int) XINT (x, i);
break;
case '0': case 't':
break;
default:
gcc_unreachable ();
}
}
return hash;
}
static inline unsigned
canon_hash (rtx x, enum machine_mode mode)
{
return hash_rtx (x, mode, &do_not_record, &hash_arg_in_memory, true);
}
static inline unsigned
safe_hash (rtx x, enum machine_mode mode)
{
int dummy_do_not_record;
return hash_rtx (x, mode, &dummy_do_not_record, NULL, true);
}
int
exp_equiv_p (rtx x, rtx y, int validate, bool for_gcse)
{
int i, j;
enum rtx_code code;
const char *fmt;
if (x == y && !validate)
return 1;
if (x == 0 || y == 0)
return x == y;
code = GET_CODE (x);
if (code != GET_CODE (y))
return 0;
if (GET_MODE (x) != GET_MODE (y))
return 0;
switch (code)
{
case PC:
case CC0:
case CONST_INT:
return x == y;
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
case REG:
if (for_gcse)
return REGNO (x) == REGNO (y);
else
{
unsigned int regno = REGNO (y);
unsigned int i;
unsigned int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: hard_regno_nregs[regno][GET_MODE (y)]);
if (REG_QTY (REGNO (x)) != REG_QTY (regno))
return 0;
if (! validate)
return 1;
for (i = regno; i < endregno; i++)
if (REG_IN_TABLE (i) != REG_TICK (i))
return 0;
return 1;
}
case MEM:
if (for_gcse)
{
if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
return 0;
if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;
}
break;
case PLUS:
case MULT:
case AND:
case IOR:
case XOR:
case NE:
case EQ:
return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0),
validate, for_gcse)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
validate, for_gcse))
|| (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
validate, for_gcse)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
validate, for_gcse)));
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;
if (GET_MODE (x) != GET_MODE (y)
|| strcmp (ASM_OPERANDS_TEMPLATE (x), ASM_OPERANDS_TEMPLATE (y))
|| strcmp (ASM_OPERANDS_OUTPUT_CONSTRAINT (x),
ASM_OPERANDS_OUTPUT_CONSTRAINT (y))
|| ASM_OPERANDS_OUTPUT_IDX (x) != ASM_OPERANDS_OUTPUT_IDX (y)
|| ASM_OPERANDS_INPUT_LENGTH (x) != ASM_OPERANDS_INPUT_LENGTH (y))
return 0;
if (ASM_OPERANDS_INPUT_LENGTH (x))
{
for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
if (! exp_equiv_p (ASM_OPERANDS_INPUT (x, i),
ASM_OPERANDS_INPUT (y, i),
validate, for_gcse)
|| strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i),
ASM_OPERANDS_INPUT_CONSTRAINT (y, i)))
return 0;
}
return 1;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'e':
if (! exp_equiv_p (XEXP (x, i), XEXP (y, i),
validate, for_gcse))
return 0;
break;
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
for (j = 0; j < XVECLEN (x, i); j++)
if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j),
validate, for_gcse))
return 0;
break;
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case '0':
case 't':
break;
default:
gcc_unreachable ();
}
}
return 1;
}
static int
cse_rtx_varies_p (rtx x, int from_alias)
{
if (REG_P (x)
&& REGNO_QTY_VALID_P (REGNO (x)))
{
int x_q = REG_QTY (REGNO (x));
struct qty_table_elem *x_ent = &qty_table[x_q];
if (GET_MODE (x) == x_ent->mode
&& x_ent->const_rtx != NULL_RTX)
return 0;
}
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& REG_P (XEXP (x, 0))
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
{
int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
struct qty_table_elem *x0_ent = &qty_table[x0_q];
if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
&& x0_ent->const_rtx != NULL_RTX)
return 0;
}
if (GET_CODE (x) == PLUS
&& REG_P (XEXP (x, 0))
&& REG_P (XEXP (x, 1))
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
{
int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
int x1_q = REG_QTY (REGNO (XEXP (x, 1)));
struct qty_table_elem *x0_ent = &qty_table[x0_q];
struct qty_table_elem *x1_ent = &qty_table[x1_q];
if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
&& x0_ent->const_rtx != NULL_RTX
&& (GET_MODE (XEXP (x, 1)) == x1_ent->mode)
&& x1_ent->const_rtx != NULL_RTX)
return 0;
}
return rtx_varies_p (x, from_alias);
}
static void
validate_canon_reg (rtx *xloc, rtx insn)
{
rtx new = canon_reg (*xloc, insn);
int insn_code;
if (insn != 0 && new != 0
&& REG_P (new) && REG_P (*xloc)
&& (((REGNO (new) < FIRST_PSEUDO_REGISTER)
!= (REGNO (*xloc) < FIRST_PSEUDO_REGISTER))
|| GET_MODE (new) != GET_MODE (*xloc)
|| (insn_code = recog_memoized (insn)) < 0
|| insn_data[insn_code].n_dups > 0))
validate_change (insn, xloc, new, 1);
else
*xloc = new;
}
static rtx
canon_reg (rtx x, rtx insn)
{
int i;
enum rtx_code code;
const char *fmt;
if (x == 0)
return x;
code = GET_CODE (x);
switch (code)
{
case PC:
case CC0:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return x;
case REG:
{
int first;
int q;
struct qty_table_elem *ent;
if (REGNO (x) < FIRST_PSEUDO_REGISTER
|| ! REGNO_QTY_VALID_P (REGNO (x)))
return x;
q = REG_QTY (REGNO (x));
ent = &qty_table[q];
first = ent->first_reg;
return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first]
: REGNO_REG_CLASS (first) == NO_REGS ? x
: gen_rtx_REG (ent->mode, first));
}
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
int j;
if (fmt[i] == 'e')
validate_canon_reg (&XEXP (x, i), insn);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
validate_canon_reg (&XVECEXP (x, i, j), insn);
}
return x;
}
static void
find_best_addr (rtx insn, rtx *loc, enum machine_mode mode)
{
struct table_elt *elt;
rtx addr = *loc;
struct table_elt *p;
int found_better = 1;
int save_do_not_record = do_not_record;
int save_hash_arg_in_memory = hash_arg_in_memory;
int addr_volatile;
int regno;
unsigned hash;
if ((GET_CODE (addr) == PLUS
&& REG_P (XEXP (addr, 0))
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
&& (regno = REGNO (XEXP (addr, 0)),
regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
|| (REG_P (addr)
&& (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
|| regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
|| CONSTANT_ADDRESS_P (addr))
return;
if (!REG_P (addr))
{
rtx folded = fold_rtx (addr, NULL_RTX);
if (folded != addr)
{
int addr_folded_cost = address_cost (folded, mode);
int addr_cost = address_cost (addr, mode);
if ((addr_folded_cost < addr_cost
|| (addr_folded_cost == addr_cost
&& (rtx_cost (folded, MEM) > rtx_cost (addr, MEM)
|| approx_reg_cost (folded) < approx_reg_cost (addr))))
&& validate_change (insn, loc, folded, 0))
addr = folded;
}
}
do_not_record = 0;
hash = HASH (addr, Pmode);
addr_volatile = do_not_record;
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
if (addr_volatile)
return;
elt = lookup (addr, hash, Pmode);
if (elt)
{
for (p = elt->first_same_value; p; p = p->next_same_value)
p->flag = 0;
while (found_better)
{
int best_addr_cost = address_cost (*loc, mode);
int best_rtx_cost = (elt->cost + 1) >> 1;
int exp_cost;
struct table_elt *best_elt = elt;
found_better = 0;
for (p = elt->first_same_value; p; p = p->next_same_value)
if (! p->flag)
{
if ((REG_P (p->exp)
|| exp_equiv_p (p->exp, p->exp, 1, false))
&& ((exp_cost = address_cost (p->exp, mode)) < best_addr_cost
|| (exp_cost == best_addr_cost
&& ((p->cost + 1) >> 1) > best_rtx_cost)))
{
found_better = 1;
best_addr_cost = exp_cost;
best_rtx_cost = (p->cost + 1) >> 1;
best_elt = p;
}
}
if (found_better)
{
if (validate_change (insn, loc,
canon_reg (copy_rtx (best_elt->exp),
NULL_RTX), 0))
return;
else
best_elt->flag = 1;
}
}
}
if (flag_expensive_optimizations
&& ARITHMETIC_P (*loc)
&& REG_P (XEXP (*loc, 0)))
{
rtx op1 = XEXP (*loc, 1);
do_not_record = 0;
hash = HASH (XEXP (*loc, 0), Pmode);
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
elt = lookup (XEXP (*loc, 0), hash, Pmode);
if (elt == 0)
return;
for (p = elt->first_same_value; p; p = p->next_same_value)
p->flag = 0;
while (found_better)
{
int best_addr_cost = address_cost (*loc, mode);
int best_rtx_cost = (COST (*loc) + 1) >> 1;
struct table_elt *best_elt = elt;
rtx best_rtx = *loc;
int count;
found_better = 0;
for (p = elt->first_same_value, count = 0;
p && count < 32;
p = p->next_same_value, count++)
if (! p->flag
&& (REG_P (p->exp)
|| exp_equiv_p (p->exp, p->exp, 1, false)))
{
rtx new = simplify_gen_binary (GET_CODE (*loc), Pmode,
p->exp, op1);
int new_cost;
new = canon_for_address (new);
new_cost = address_cost (new, mode);
if (new_cost < best_addr_cost
|| (new_cost == best_addr_cost
&& (COST (new) + 1) >> 1 > best_rtx_cost))
{
found_better = 1;
best_addr_cost = new_cost;
best_rtx_cost = (COST (new) + 1) >> 1;
best_elt = p;
best_rtx = new;
}
}
if (found_better)
{
if (validate_change (insn, loc,
canon_reg (copy_rtx (best_rtx),
NULL_RTX), 0))
return;
else
best_elt->flag = 1;
}
}
}
}
static enum rtx_code
find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
enum machine_mode *pmode1, enum machine_mode *pmode2)
{
rtx arg1, arg2;
arg1 = *parg1, arg2 = *parg2;
while (arg2 == CONST0_RTX (GET_MODE (arg1)))
{
rtx x = 0;
int reverse_code = 0;
struct table_elt *p = 0;
if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx)
x = arg1;
else if (COMPARISON_P (arg1))
{
#ifdef FLOAT_STORE_FLAG_VALUE
REAL_VALUE_TYPE fsfv;
#endif
if (code == NE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
&& code == LT && STORE_FLAG_VALUE == -1)
#ifdef FLOAT_STORE_FLAG_VALUE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
&& (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
x = arg1;
else if (code == EQ
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
&& code == GE && STORE_FLAG_VALUE == -1)
#ifdef FLOAT_STORE_FLAG_VALUE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
&& (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
x = arg1, reverse_code = 1;
}
if (x == 0)
p = lookup (arg1, SAFE_HASH (arg1, GET_MODE (arg1)), GET_MODE (arg1));
if (p)
{
p = p->first_same_value;
if (p->is_const)
break;
}
for (; p; p = p->next_same_value)
{
enum machine_mode inner_mode = GET_MODE (p->exp);
#ifdef FLOAT_STORE_FLAG_VALUE
REAL_VALUE_TYPE fsfv;
#endif
if (! exp_equiv_p (p->exp, p->exp, 1, false))
continue;
if (GET_CODE (p->exp) == COMPARE
|| ((code == NE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_INT
&& (GET_MODE_BITSIZE (inner_mode)
<= HOST_BITS_PER_WIDE_INT)
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
&& (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
&& COMPARISON_P (p->exp)))
{
x = p->exp;
break;
}
else if ((code == EQ
|| (code == GE
&& GET_MODE_CLASS (inner_mode) == MODE_INT
&& (GET_MODE_BITSIZE (inner_mode)
<= HOST_BITS_PER_WIDE_INT)
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == GE
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
&& (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
&& COMPARISON_P (p->exp))
{
reverse_code = 1;
x = p->exp;
break;
}
else if (!rtx_addr_can_trap_p (p->exp))
{
arg1 = p->exp;
continue;
}
}
if (x == 0)
break;
if (reverse_code)
{
enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
if (reversed == UNKNOWN)
break;
else
code = reversed;
}
else if (COMPARISON_P (x))
code = GET_CODE (x);
arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
}
*pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
*parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
return code;
}
static rtx
fold_rtx (rtx x, rtx insn)
{
enum rtx_code code;
enum machine_mode mode;
const char *fmt;
int i;
rtx new = 0;
int copied = 0;
int must_swap = 0;
rtx folded_arg0;
rtx folded_arg1;
rtx const_arg0;
rtx const_arg1;
rtx const_arg2;
enum machine_mode mode_arg0;
if (x == 0)
return x;
mode = GET_MODE (x);
code = GET_CODE (x);
switch (code)
{
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case REG:
case PC:
case EXPR_LIST:
return x;
#ifdef HAVE_cc0
case CC0:
return prev_insn_cc0;
#endif
case SUBREG:
if ((new = lookup_as_function (x, CONST_INT)) != 0
|| (new = lookup_as_function (x, CONST_DOUBLE)) != 0)
return new;
if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
{
enum machine_mode imode = GET_MODE (SUBREG_REG (x));
struct table_elt *elt;
if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
&& GET_MODE_SIZE (imode) <= UNITS_PER_WORD
&& (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode),
imode)) != 0)
for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
{
if (CONSTANT_P (elt->exp)
&& GET_MODE (elt->exp) == VOIDmode)
return elt->exp;
if (GET_CODE (elt->exp) == SUBREG
&& GET_MODE (SUBREG_REG (elt->exp)) == mode
&& exp_equiv_p (elt->exp, elt->exp, 1, false))
return copy_rtx (SUBREG_REG (elt->exp));
}
return x;
}
folded_arg0 = fold_rtx (SUBREG_REG (x), insn);
const_arg0 = equiv_constant (folded_arg0);
if (const_arg0)
folded_arg0 = const_arg0;
if (folded_arg0 != SUBREG_REG (x))
{
new = simplify_subreg (mode, folded_arg0,
GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
if (new)
return new;
}
if (REG_P (folded_arg0)
&& GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0)))
{
struct table_elt *elt;
elt = lookup (folded_arg0,
HASH (folded_arg0, GET_MODE (folded_arg0)),
GET_MODE (folded_arg0));
if (elt)
elt = elt->first_same_value;
if (subreg_lowpart_p (x))
for (; elt; elt = elt->next_same_value)
{
enum rtx_code eltcode = GET_CODE (elt->exp);
if (UNARY_P (elt->exp)
&& eltcode != SIGN_EXTEND
&& eltcode != ZERO_EXTEND
&& GET_CODE (XEXP (elt->exp, 0)) == SUBREG
&& GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode
&& (GET_MODE_CLASS (mode)
== GET_MODE_CLASS (GET_MODE (XEXP (elt->exp, 0)))))
{
rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
if (!REG_P (op0) && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
op0 = equiv_constant (op0);
if (op0)
new = simplify_unary_operation (GET_CODE (elt->exp), mode,
op0, mode);
}
else if (ARITHMETIC_P (elt->exp)
&& eltcode != DIV && eltcode != MOD
&& eltcode != UDIV && eltcode != UMOD
&& eltcode != ASHIFTRT && eltcode != LSHIFTRT
&& eltcode != ROTATE && eltcode != ROTATERT
&& ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG
&& (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0)))
== mode))
|| CONSTANT_P (XEXP (elt->exp, 0)))
&& ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG
&& (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1)))
== mode))
|| CONSTANT_P (XEXP (elt->exp, 1))))
{
rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
if (op0 && !REG_P (op0) && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
if (op0)
op0 = equiv_constant (op0);
if (op1 && !REG_P (op1) && ! CONSTANT_P (op1))
op1 = fold_rtx (op1, NULL_RTX);
if (op1)
op1 = equiv_constant (op1);
if (op0 && op1
&& GET_CODE (elt->exp) == ASHIFT
&& GET_CODE (op1) == CONST_INT
&& INTVAL (op1) >= GET_MODE_BITSIZE (mode))
{
if (INTVAL (op1)
< GET_MODE_BITSIZE (GET_MODE (elt->exp)))
new = CONST0_RTX (mode);
else
new = 0;
}
else if (op0 && op1)
new = simplify_binary_operation (GET_CODE (elt->exp), mode, op0, op1);
}
else if (GET_CODE (elt->exp) == SUBREG
&& GET_MODE (SUBREG_REG (elt->exp)) == mode
&& (GET_MODE_SIZE (GET_MODE (folded_arg0))
<= UNITS_PER_WORD)
&& exp_equiv_p (elt->exp, elt->exp, 1, false))
new = copy_rtx (SUBREG_REG (elt->exp));
if (new)
return new;
}
else
for (; elt; elt = elt->next_same_value)
{
if (GET_CODE (elt->exp) == ZERO_EXTEND
&& subreg_lsb (x)
>= GET_MODE_BITSIZE (GET_MODE (XEXP (elt->exp, 0))))
return CONST0_RTX (mode);
}
}
return x;
case NOT:
case NEG:
new = lookup_as_function (XEXP (x, 0), code);
if (new)
return fold_rtx (copy_rtx (XEXP (new, 0)), insn);
break;
case MEM:
if (insn != 0)
find_best_addr (insn, &XEXP (x, 0), GET_MODE (x));
{
rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
rtx base = 0;
HOST_WIDE_INT offset = 0;
if (REG_P (addr)
&& REGNO_QTY_VALID_P (REGNO (addr)))
{
int addr_q = REG_QTY (REGNO (addr));
struct qty_table_elem *addr_ent = &qty_table[addr_q];
if (GET_MODE (addr) == addr_ent->mode
&& addr_ent->const_rtx != NULL_RTX)
addr = addr_ent->const_rtx;
}
addr = targetm.delegitimize_address (addr);
if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
base = addr;
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
{
base = XEXP (XEXP (addr, 0), 0);
offset = INTVAL (XEXP (XEXP (addr, 0), 1));
}
else if (GET_CODE (addr) == LO_SUM
&& GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
base = XEXP (addr, 1);
if (base && GET_CODE (base) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (base))
{
rtx constant = get_pool_constant (base);
enum machine_mode const_mode = get_pool_mode (base);
rtx new;
if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT)
{
constant_pool_entries_cost = COST (constant);
constant_pool_entries_regcost = approx_reg_cost (constant);
}
if (offset == 0 && mode == const_mode)
return constant;
if (! CONSTANT_P (constant))
return x;
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) == UNITS_PER_WORD
&& offset % UNITS_PER_WORD == 0
&& (new = operand_subword (constant,
offset / UNITS_PER_WORD,
0, const_mode)) != 0)
return new;
if (((BYTES_BIG_ENDIAN
&& offset == GET_MODE_SIZE (GET_MODE (constant)) - 1)
|| (! BYTES_BIG_ENDIAN && offset == 0))
&& (new = gen_lowpart (mode, constant)) != 0)
return new;
}
if (base && GET_CODE (base) == LABEL_REF)
{
rtx label = XEXP (base, 0);
rtx table_insn = NEXT_INSN (label);
if (table_insn && JUMP_P (table_insn)
&& GET_CODE (PATTERN (table_insn)) == ADDR_VEC)
{
rtx table = PATTERN (table_insn);
if (offset >= 0
&& (offset / GET_MODE_SIZE (GET_MODE (table))
< XVECLEN (table, 0)))
{
rtx label = XVECEXP
(table, 0, offset / GET_MODE_SIZE (GET_MODE (table)));
rtx set;
if (!insn)
return label;
set = single_set (insn);
if (! set || SET_SRC (set) != x)
return x;
if (SET_DEST (set) == pc_rtx)
return label;
return x;
}
}
if (table_insn && JUMP_P (table_insn)
&& GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC)
{
rtx table = PATTERN (table_insn);
if (offset >= 0
&& (offset / GET_MODE_SIZE (GET_MODE (table))
< XVECLEN (table, 1)))
{
offset /= GET_MODE_SIZE (GET_MODE (table));
new = gen_rtx_MINUS (Pmode, XVECEXP (table, 1, offset),
XEXP (table, 0));
if (GET_MODE (table) != Pmode)
new = gen_rtx_TRUNCATE (GET_MODE (table), new);
return gen_rtx_CONST (GET_MODE (new), new);
}
}
}
return x;
}
#ifdef NO_FUNCTION_CSE
case CALL:
if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
return x;
break;
#endif
case ASM_OPERANDS:
if (insn)
{
for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
validate_change (insn, &ASM_OPERANDS_INPUT (x, i),
fold_rtx (ASM_OPERANDS_INPUT (x, i), insn), 0);
}
break;
default:
break;
}
const_arg0 = 0;
const_arg1 = 0;
const_arg2 = 0;
mode_arg0 = VOIDmode;
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
{
rtx arg = XEXP (x, i);
rtx folded_arg = arg, const_arg = 0;
enum machine_mode mode_arg = GET_MODE (arg);
rtx cheap_arg, expensive_arg;
rtx replacements[2];
int j;
int old_cost = COST_IN (XEXP (x, i), code);
switch (GET_CODE (arg))
{
case REG:
if (REGNO_QTY_VALID_P (REGNO (arg)))
{
int arg_q = REG_QTY (REGNO (arg));
struct qty_table_elem *arg_ent = &qty_table[arg_q];
if (arg_ent->const_rtx != NULL_RTX
&& !REG_P (arg_ent->const_rtx)
&& GET_CODE (arg_ent->const_rtx) != PLUS)
const_arg
= gen_lowpart (GET_MODE (arg),
arg_ent->const_rtx);
}
break;
case CONST:
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
case CONST_VECTOR:
const_arg = arg;
break;
#ifdef HAVE_cc0
case CC0:
folded_arg = prev_insn_cc0;
mode_arg = prev_insn_cc0_mode;
const_arg = equiv_constant (folded_arg);
break;
#endif
default:
folded_arg = fold_rtx (arg, insn);
const_arg = equiv_constant (folded_arg);
}
switch (i)
{
case 0:
folded_arg0 = folded_arg;
const_arg0 = const_arg;
mode_arg0 = mode_arg;
break;
case 1:
folded_arg1 = folded_arg;
const_arg1 = const_arg;
break;
case 2:
const_arg2 = const_arg;
break;
}
if (const_arg == 0 || const_arg == folded_arg
|| COST_IN (const_arg, code) > COST_IN (folded_arg, code))
cheap_arg = folded_arg, expensive_arg = const_arg;
else
cheap_arg = const_arg, expensive_arg = folded_arg;
if (cheap_arg == XEXP (x, i))
continue;
if (insn == 0 && ! copied)
{
x = copy_rtx (x);
copied = 1;
}
replacements[0] = cheap_arg;
replacements[1] = expensive_arg;
for (j = 0; j < 2 && replacements[j]; j++)
{
int new_cost = COST_IN (replacements[j], code);
if (new_cost > old_cost
|| (new_cost == old_cost && CONSTANT_P (XEXP (x, i))))
break;
if (GET_RTX_CLASS (code) == RTX_UNARY
&& GET_MODE (replacements[j]) != mode_arg0
&& (code == ZERO_EXTEND
|| code == SIGN_EXTEND
|| code == TRUNCATE
|| code == FLOAT_TRUNCATE
|| code == FLOAT_EXTEND
|| code == FLOAT
|| code == FIX
|| code == UNSIGNED_FLOAT
|| code == UNSIGNED_FIX))
continue;
if (validate_change (insn, &XEXP (x, i), replacements[j], 0))
break;
if (GET_RTX_CLASS (code) == RTX_COMM_COMPARE
|| GET_RTX_CLASS (code) == RTX_COMM_ARITH)
{
validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1);
validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1);
if (apply_change_group ())
{
rtx tem;
tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1);
XEXP (x, 1) = tem;
must_swap = 1;
break;
}
}
}
}
else
{
if (fmt[i] == 'E')
{;}
}
if (COMMUTATIVE_P (x))
{
if (must_swap
|| swap_commutative_operands_p (const_arg0 ? const_arg0
: XEXP (x, 0),
const_arg1 ? const_arg1
: XEXP (x, 1)))
{
rtx tem = XEXP (x, 0);
if (insn == 0 && ! copied)
{
x = copy_rtx (x);
copied = 1;
}
validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
validate_change (insn, &XEXP (x, 1), tem, 1);
if (apply_change_group ())
{
tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
}
}
}
switch (GET_RTX_CLASS (code))
{
case RTX_UNARY:
{
int is_const = 0;
if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
&& mode_arg0 == VOIDmode)
break;
if (const_arg0 != 0 && GET_CODE (const_arg0) == CONST)
is_const = 1, const_arg0 = XEXP (const_arg0, 0);
new = simplify_unary_operation (code, mode,
const_arg0 ? const_arg0 : folded_arg0,
mode_arg0);
if (new != 0 && is_const
&& GET_CODE (new) == PLUS
&& (GET_CODE (XEXP (new, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (new, 0)) == LABEL_REF)
&& GET_CODE (XEXP (new, 1)) == CONST_INT)
new = gen_rtx_CONST (mode, new);
}
break;
case RTX_COMPARE:
case RTX_COMM_COMPARE:
if (VECTOR_MODE_P (mode))
break;
if (const_arg0 == 0 || const_arg1 == 0)
{
struct table_elt *p0, *p1;
rtx true_rtx = const_true_rtx, false_rtx = const0_rtx;
enum machine_mode mode_arg1;
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
(FLOAT_STORE_FLAG_VALUE (mode), mode));
false_rtx = CONST0_RTX (mode);
}
#endif
code = find_comparison_args (code, &folded_arg0, &folded_arg1,
&mode_arg0, &mode_arg1);
if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC)
break;
const_arg0 = equiv_constant (folded_arg0);
const_arg1 = equiv_constant (folded_arg1);
if (const_arg0 == 0 || const_arg1 == 0)
{
if (const_arg1 == const0_rtx
&& nonzero_address_p (folded_arg0))
{
if (code == EQ)
return false_rtx;
else if (code == NE)
return true_rtx;
}
if (folded_arg0 == folded_arg1
|| (REG_P (folded_arg0)
&& REG_P (folded_arg1)
&& (REG_QTY (REGNO (folded_arg0))
== REG_QTY (REGNO (folded_arg1))))
|| ((p0 = lookup (folded_arg0,
SAFE_HASH (folded_arg0, mode_arg0),
mode_arg0))
&& (p1 = lookup (folded_arg1,
SAFE_HASH (folded_arg1, mode_arg0),
mode_arg0))
&& p0->first_same_value == p1->first_same_value))
{
if (!HONOR_NANS (mode_arg0))
return ((code == EQ || code == LE || code == GE
|| code == LEU || code == GEU || code == UNEQ
|| code == UNLE || code == UNGE
|| code == ORDERED)
? true_rtx : false_rtx);
if (code == UNEQ || code == UNLE || code == UNGE)
return true_rtx;
if (code == LTGT || code == LT || code == GT)
return false_rtx;
}
else if (REG_P (folded_arg0))
{
int qty = REG_QTY (REGNO (folded_arg0));
if (REGNO_QTY_VALID_P (REGNO (folded_arg0)))
{
struct qty_table_elem *ent = &qty_table[qty];
if ((comparison_dominates_p (ent->comparison_code, code)
|| (! FLOAT_MODE_P (mode_arg0)
&& comparison_dominates_p (ent->comparison_code,
reverse_condition (code))))
&& (rtx_equal_p (ent->comparison_const, folded_arg1)
|| (const_arg1
&& rtx_equal_p (ent->comparison_const,
const_arg1))
|| (REG_P (folded_arg1)
&& (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty))))
return (comparison_dominates_p (ent->comparison_code, code)
? true_rtx : false_rtx);
}
}
}
}
if (const_arg1 == const0_rtx)
{
rtx y = lookup_as_function (folded_arg0, IOR);
rtx inner_const;
if (y != 0
&& (inner_const = equiv_constant (XEXP (y, 1))) != 0
&& GET_CODE (inner_const) == CONST_INT
&& INTVAL (inner_const) != 0)
{
int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1;
int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
&& (INTVAL (inner_const)
& ((HOST_WIDE_INT) 1 << sign_bitnum)));
rtx true_rtx = const_true_rtx, false_rtx = const0_rtx;
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
(FLOAT_STORE_FLAG_VALUE (mode), mode));
false_rtx = CONST0_RTX (mode);
}
#endif
switch (code)
{
case EQ:
return false_rtx;
case NE:
return true_rtx;
case LT: case LE:
if (has_sign)
return true_rtx;
break;
case GT: case GE:
if (has_sign)
return false_rtx;
break;
default:
break;
}
}
}
{
rtx op0 = const_arg0 ? const_arg0 : folded_arg0;
rtx op1 = const_arg1 ? const_arg1 : folded_arg1;
new = simplify_relational_operation (code, mode, mode_arg0, op0, op1);
}
break;
case RTX_BIN_ARITH:
case RTX_COMM_ARITH:
switch (code)
{
case PLUS:
if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
{
rtx y
= GET_CODE (folded_arg0) == MINUS ? folded_arg0
: lookup_as_function (folded_arg0, MINUS);
if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
&& XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0))
return XEXP (y, 0);
if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0
: lookup_as_function (folded_arg0, CONST))) != 0
&& GET_CODE (XEXP (y, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
&& XEXP (XEXP (XEXP (y, 0), 1), 0) == XEXP (const_arg1, 0))
return XEXP (XEXP (y, 0), 0);
}
if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF)
{
rtx y
= GET_CODE (folded_arg1) == MINUS ? folded_arg1
: lookup_as_function (folded_arg1, MINUS);
if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
&& XEXP (XEXP (y, 1), 0) == XEXP (const_arg0, 0))
return XEXP (y, 0);
if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1
: lookup_as_function (folded_arg1, CONST))) != 0
&& GET_CODE (XEXP (y, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
&& XEXP (XEXP (XEXP (y, 0), 1), 0) == XEXP (const_arg0, 0))
return XEXP (XEXP (y, 0), 0);
}
if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT
&& INTVAL (const_arg1) < 0
&& INTVAL (const_arg1) !=
((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
&& REG_P (folded_arg1))
{
rtx new_const = GEN_INT (-INTVAL (const_arg1));
struct table_elt *p
= lookup (new_const, SAFE_HASH (new_const, mode), mode);
if (p)
for (p = p->first_same_value; p; p = p->next_same_value)
if (REG_P (p->exp))
return simplify_gen_binary (MINUS, mode, folded_arg0,
canon_reg (p->exp, NULL_RTX));
}
goto from_plus;
case MINUS:
if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
{
rtx y = lookup_as_function (XEXP (x, 0), PLUS);
if (y && GET_CODE (XEXP (y, 1)) == CONST_INT)
return fold_rtx (plus_constant (copy_rtx (y),
-INTVAL (const_arg1)),
NULL_RTX);
}
from_plus:
case SMIN: case SMAX: case UMIN: case UMAX:
case IOR: case AND: case XOR:
case MULT:
case ASHIFT: case LSHIFTRT: case ASHIFTRT:
if (REG_P (folded_arg0)
&& const_arg1 && GET_CODE (const_arg1) == CONST_INT)
{
int is_shift
= (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT);
rtx y = lookup_as_function (folded_arg0, code);
rtx inner_const;
enum rtx_code associate_code;
rtx new_const;
if (y == 0
|| 0 == (inner_const
= equiv_constant (fold_rtx (XEXP (y, 1), 0)))
|| GET_CODE (inner_const) != CONST_INT
|| XEXP (y, 0) == folded_arg0)
break;
if (code == PLUS && const_arg1 == inner_const
&& ((HAVE_PRE_INCREMENT
&& exact_log2 (INTVAL (const_arg1)) >= 0)
|| (HAVE_POST_INCREMENT
&& exact_log2 (INTVAL (const_arg1)) >= 0)
|| (HAVE_PRE_DECREMENT
&& exact_log2 (- INTVAL (const_arg1)) >= 0)
|| (HAVE_POST_DECREMENT
&& exact_log2 (- INTVAL (const_arg1)) >= 0)))
break;
associate_code = (is_shift || code == MINUS ? PLUS : code);
new_const = simplify_binary_operation (associate_code, mode,
const_arg1, inner_const);
if (new_const == 0)
break;
if (is_shift && GET_CODE (new_const) == CONST_INT
&& INTVAL (new_const) >= GET_MODE_BITSIZE (mode))
{
if (code == ASHIFTRT)
new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
else
break;
}
y = copy_rtx (XEXP (y, 0));
if (! reg_mentioned_p (folded_arg0, y))
y = fold_rtx (y, insn);
return simplify_gen_binary (code, mode, y, new_const);
}
break;
case DIV: case UDIV:
break;
default:
break;
}
new = simplify_binary_operation (code, mode,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1);
break;
case RTX_OBJ:
if (code == LO_SUM && const_arg0 != 0
&& GET_CODE (const_arg0) == HIGH
&& rtx_equal_p (XEXP (const_arg0, 0), const_arg1))
return const_arg1;
break;
case RTX_TERNARY:
case RTX_BITFIELD_OPS:
new = simplify_ternary_operation (code, mode, mode_arg0,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1,
const_arg2 ? const_arg2 : XEXP (x, 2));
break;
default:
break;
}
return new ? new : x;
}
static rtx
equiv_constant (rtx x)
{
if (REG_P (x)
&& REGNO_QTY_VALID_P (REGNO (x)))
{
int x_q = REG_QTY (REGNO (x));
struct qty_table_elem *x_ent = &qty_table[x_q];
if (x_ent->const_rtx)
x = gen_lowpart (GET_MODE (x), x_ent->const_rtx);
}
if (x == 0 || CONSTANT_P (x))
return x;
if (MEM_P (x))
{
struct table_elt *elt;
x = fold_rtx (x, NULL_RTX);
if (CONSTANT_P (x))
return x;
elt = lookup (x, SAFE_HASH (x, GET_MODE (x)), GET_MODE (x));
if (elt == 0)
return 0;
for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
if (elt->is_const && CONSTANT_P (elt->exp))
return elt->exp;
}
return 0;
}
rtx
gen_lowpart_if_possible (enum machine_mode mode, rtx x)
{
rtx result = gen_lowpart_common (mode, x);
if (result)
return result;
else if (MEM_P (x))
{
int offset = 0;
rtx new;
if (WORDS_BIG_ENDIAN)
offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
if (BYTES_BIG_ENDIAN)
offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
- MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
new = adjust_address_nv (x, mode, offset);
if (! memory_address_p (mode, XEXP (new, 0)))
return 0;
return new;
}
else
return 0;
}
static void
record_jump_equiv (rtx insn, int taken)
{
int cond_known_true;
rtx op0, op1;
rtx set;
enum machine_mode mode, mode0, mode1;
int reversed_nonequality = 0;
enum rtx_code code;
if (! any_condjump_p (insn))
return;
set = pc_set (insn);
if (taken)
cond_known_true = (XEXP (SET_SRC (set), 2) == pc_rtx);
else
cond_known_true = (XEXP (SET_SRC (set), 1) == pc_rtx);
code = GET_CODE (XEXP (SET_SRC (set), 0));
op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn);
op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn);
code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
if (! cond_known_true)
{
code = reversed_comparison_code_parts (code, op0, op1, insn);
if (code == UNKNOWN)
return;
}
mode = mode0;
if (mode1 != VOIDmode)
mode = mode1;
record_jump_cond (code, mode, op0, op1, reversed_nonequality);
}
static rtx
record_jump_cond_subreg (enum machine_mode mode, rtx op)
{
enum machine_mode op_mode = GET_MODE (op);
if (op_mode == mode || op_mode == VOIDmode)
return op;
return lowpart_subreg (mode, op, op_mode);
}
static void
record_jump_cond (enum rtx_code code, enum machine_mode mode, rtx op0,
rtx op1, int reversed_nonequality)
{
unsigned op0_hash, op1_hash;
int op0_in_memory, op1_in_memory;
struct table_elt *op0_elt, *op1_elt;
if (code == EQ && GET_CODE (op0) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
rtx tem = record_jump_cond_subreg (inner_mode, op1);
if (tem)
record_jump_cond (code, mode, SUBREG_REG (op0), tem,
reversed_nonequality);
}
if (code == EQ && GET_CODE (op1) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (op1))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
rtx tem = record_jump_cond_subreg (inner_mode, op0);
if (tem)
record_jump_cond (code, mode, SUBREG_REG (op1), tem,
reversed_nonequality);
}
if (code == NE && GET_CODE (op0) == SUBREG
&& subreg_lowpart_p (op0)
&& (GET_MODE_SIZE (GET_MODE (op0))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
rtx tem = record_jump_cond_subreg (inner_mode, op1);
if (tem)
record_jump_cond (code, mode, SUBREG_REG (op0), tem,
reversed_nonequality);
}
if (code == NE && GET_CODE (op1) == SUBREG
&& subreg_lowpart_p (op1)
&& (GET_MODE_SIZE (GET_MODE (op1))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
rtx tem = record_jump_cond_subreg (inner_mode, op0);
if (tem)
record_jump_cond (code, mode, SUBREG_REG (op1), tem,
reversed_nonequality);
}
do_not_record = 0;
hash_arg_in_memory = 0;
op0_hash = HASH (op0, mode);
op0_in_memory = hash_arg_in_memory;
if (do_not_record)
return;
do_not_record = 0;
hash_arg_in_memory = 0;
op1_hash = HASH (op1, mode);
op1_in_memory = hash_arg_in_memory;
if (do_not_record)
return;
op0_elt = lookup (op0, op0_hash, mode);
op1_elt = lookup (op1, op1_hash, mode);
if ((op0_elt != 0 && op1_elt != 0
&& op0_elt->first_same_value == op1_elt->first_same_value)
|| op0 == op1 || rtx_equal_p (op0, op1))
return;
if (code != EQ || FLOAT_MODE_P (GET_MODE (op0)))
{
struct qty_table_elem *ent;
int qty;
if (!REG_P (op1))
op1 = equiv_constant (op1);
if ((reversed_nonequality && FLOAT_MODE_P (mode))
|| !REG_P (op0) || op1 == 0)
return;
if (op0_elt == 0)
{
if (insert_regs (op0, NULL, 0))
{
rehash_using_reg (op0);
op0_hash = HASH (op0, mode);
if (! CONSTANT_P (op1))
op1_hash = HASH (op1,mode);
}
op0_elt = insert (op0, NULL, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
}
qty = REG_QTY (REGNO (op0));
ent = &qty_table[qty];
ent->comparison_code = code;
if (REG_P (op1))
{
op1_elt = lookup (op1, op1_hash, mode);
if (op1_elt == 0)
{
if (insert_regs (op1, NULL, 0))
{
rehash_using_reg (op1);
op1_hash = HASH (op1, mode);
}
op1_elt = insert (op1, NULL, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
}
ent->comparison_const = NULL_RTX;
ent->comparison_qty = REG_QTY (REGNO (op1));
}
else
{
ent->comparison_const = op1;
ent->comparison_qty = -1;
}
return;
}
if (op0_elt == 0)
{
if (insert_regs (op0, NULL, 0))
{
rehash_using_reg (op0);
op0_hash = HASH (op0, mode);
}
op0_elt = insert (op0, NULL, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
}
if (op1_elt == 0)
{
if (insert_regs (op1, NULL, 0))
{
rehash_using_reg (op1);
op1_hash = HASH (op1, mode);
}
op1_elt = insert (op1, NULL, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
}
merge_equiv_classes (op0_elt, op1_elt);
}
struct set
{
rtx rtl;
rtx src;
struct table_elt *src_elt;
unsigned src_hash;
unsigned dest_hash;
rtx inner_dest;
char src_in_memory;
char src_volatile;
ENUM_BITFIELD(machine_mode) mode : 8;
rtx src_const;
rtx orig_src;
unsigned src_const_hash;
struct table_elt *src_const_elt;
};
static void
cse_insn (rtx insn, rtx libcall_insn)
{
rtx x = PATTERN (insn);
int i;
rtx tem;
int n_sets = 0;
#ifdef HAVE_cc0
rtx this_insn_cc0 = 0;
enum machine_mode this_insn_cc0_mode = VOIDmode;
#endif
rtx src_eqv = 0;
struct table_elt *src_eqv_elt = 0;
int src_eqv_volatile = 0;
int src_eqv_in_memory = 0;
unsigned src_eqv_hash = 0;
struct set *sets = (struct set *) 0;
this_insn = insn;
if (CALL_P (insn))
{
for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
{
if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn);
}
}
if (GET_CODE (x) == SET)
{
sets = alloca (sizeof (struct set));
sets[0].rtl = x;
if (SET_DEST (x) == pc_rtx
&& GET_CODE (SET_SRC (x)) == LABEL_REF)
;
else if (GET_CODE (SET_SRC (x)) == CALL)
{
canon_reg (SET_SRC (x), insn);
apply_change_group ();
fold_rtx (SET_SRC (x), insn);
invalidate (SET_DEST (x), VOIDmode);
}
else
n_sets = 1;
}
else if (GET_CODE (x) == PARALLEL)
{
int lim = XVECLEN (x, 0);
sets = alloca (lim * sizeof (struct set));
for (i = 0; i < lim; i++)
{
rtx y = XVECEXP (x, 0, i);
if (GET_CODE (y) == CLOBBER)
{
rtx clobbered = XEXP (y, 0);
if (REG_P (clobbered)
|| GET_CODE (clobbered) == SUBREG)
invalidate (clobbered, VOIDmode);
else if (GET_CODE (clobbered) == STRICT_LOW_PART
|| GET_CODE (clobbered) == ZERO_EXTRACT)
invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
}
}
for (i = 0; i < lim; i++)
{
rtx y = XVECEXP (x, 0, i);
if (GET_CODE (y) == SET)
{
if (GET_CODE (SET_SRC (y)) == CALL)
{
canon_reg (SET_SRC (y), insn);
apply_change_group ();
fold_rtx (SET_SRC (y), insn);
invalidate (SET_DEST (y), VOIDmode);
}
else if (SET_DEST (y) == pc_rtx
&& GET_CODE (SET_SRC (y)) == LABEL_REF)
;
else
sets[n_sets++].rtl = y;
}
else if (GET_CODE (y) == CLOBBER)
{
if (MEM_P (XEXP (y, 0)))
canon_reg (XEXP (y, 0), NULL_RTX);
}
else if (GET_CODE (y) == USE
&& ! (REG_P (XEXP (y, 0))
&& REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
canon_reg (y, NULL_RTX);
else if (GET_CODE (y) == CALL)
{
canon_reg (y, insn);
apply_change_group ();
fold_rtx (y, insn);
}
}
}
else if (GET_CODE (x) == CLOBBER)
{
if (MEM_P (XEXP (x, 0)))
canon_reg (XEXP (x, 0), NULL_RTX);
}
else if (GET_CODE (x) == USE
&& ! (REG_P (XEXP (x, 0))
&& REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
canon_reg (XEXP (x, 0), NULL_RTX);
else if (GET_CODE (x) == CALL)
{
canon_reg (x, insn);
apply_change_group ();
fold_rtx (x, insn);
}
if (n_sets == 1 && REG_NOTES (insn) != 0
&& (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
&& (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
|| GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
{
src_eqv = fold_rtx (canon_reg (XEXP (tem, 0), NULL_RTX), insn);
XEXP (tem, 0) = src_eqv;
}
for (i = 0; i < n_sets; i++)
{
rtx dest = SET_DEST (sets[i].rtl);
rtx src = SET_SRC (sets[i].rtl);
rtx new = canon_reg (src, insn);
int insn_code;
sets[i].orig_src = src;
if ((REG_P (new) && REG_P (src)
&& ((REGNO (new) < FIRST_PSEUDO_REGISTER)
!= (REGNO (src) < FIRST_PSEUDO_REGISTER)))
|| (insn_code = recog_memoized (insn)) < 0
|| insn_data[insn_code].n_dups > 0)
validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
else
SET_SRC (sets[i].rtl) = new;
if (GET_CODE (dest) == ZERO_EXTRACT)
{
validate_change (insn, &XEXP (dest, 1),
canon_reg (XEXP (dest, 1), insn), 1);
validate_change (insn, &XEXP (dest, 2),
canon_reg (XEXP (dest, 2), insn), 1);
}
while (GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (MEM_P (dest))
canon_reg (dest, insn);
}
apply_change_group ();
for (i = 0; i < n_sets; i++)
{
rtx src, dest;
rtx src_folded;
struct table_elt *elt = 0, *p;
enum machine_mode mode;
rtx src_eqv_here;
rtx src_const = 0;
rtx src_related = 0;
rtx zero_sign_extended_src = NULL_RTX;
struct table_elt *src_const_elt = 0;
int src_cost = MAX_COST;
int src_eqv_cost = MAX_COST;
int src_folded_cost = MAX_COST;
int src_related_cost = MAX_COST;
int src_elt_cost = MAX_COST;
int src_regcost = MAX_COST;
int src_eqv_regcost = MAX_COST;
int src_folded_regcost = MAX_COST;
int src_related_regcost = MAX_COST;
int src_elt_regcost = MAX_COST;
int src_folded_force_flag = 0;
dest = SET_DEST (sets[i].rtl);
src = SET_SRC (sets[i].rtl);
mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
sets[i].mode = mode;
if (src_eqv)
{
enum machine_mode eqvmode = mode;
if (GET_CODE (dest) == STRICT_LOW_PART)
eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
do_not_record = 0;
hash_arg_in_memory = 0;
src_eqv_hash = HASH (src_eqv, eqvmode);
if (!do_not_record)
src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode);
src_eqv_volatile = do_not_record;
src_eqv_in_memory = hash_arg_in_memory;
}
if (GET_CODE (dest) == STRICT_LOW_PART)
src_eqv_here = 0;
else
src_eqv_here = src_eqv;
src_folded = fold_rtx (src, insn);
#if 0
if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT)
{
rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
if (GET_CODE (src) == CONST_INT
&& GET_CODE (width) == CONST_INT
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
src_folded
= GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
<< INTVAL (width)) - 1));
}
#endif
do_not_record = 0;
hash_arg_in_memory = 0;
sets[i].src = src;
sets[i].src_hash = HASH (src, mode);
sets[i].src_volatile = do_not_record;
sets[i].src_in_memory = hash_arg_in_memory;
if (MEM_P (src)
&& find_reg_note (insn, REG_EQUIV, NULL_RTX) != 0
&& REG_P (dest)
&& REGNO (dest) >= FIRST_PSEUDO_REGISTER)
sets[i].src_volatile = 1;
#if 0
if (GET_CODE (src) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (src))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))
sets[i].src_volatile = 1;
#endif
if (!sets[i].src_volatile)
{
elt = lookup (src, sets[i].src_hash, mode);
if (!elt
&& (GET_CODE(src) == ZERO_EXTEND || GET_CODE(src) == SIGN_EXTEND)
&& GET_CODE (XEXP (src, 0)) == MEM)
{
unsigned mem_hash;
rtx nsrc = XEXP (src, 0);
enum machine_mode nmode = GET_MODE(nsrc);
do_not_record = 0;
hash_arg_in_memory = 0;
mem_hash = HASH (nsrc, nmode);
elt = lookup (nsrc, mem_hash, nmode);
if (elt)
{
sets[i].src = nsrc;
sets[i].src_hash = mem_hash;
sets[i].src_volatile = do_not_record;
sets[i].src_in_memory = hash_arg_in_memory;
zero_sign_extended_src = src;
src = nsrc;
mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
sets[i].mode = mode;
src_folded = fold_rtx (src, insn);
}
}
}
sets[i].src_elt = elt;
if (elt && src_eqv_here && src_eqv_elt)
{
if (elt->first_same_value != src_eqv_elt->first_same_value)
{
merge_equiv_classes (elt, src_eqv_elt);
src_eqv_hash = HASH (src_eqv, elt->mode);
src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode);
}
src_eqv_here = 0;
}
else if (src_eqv_elt)
elt = src_eqv_elt;
if (elt)
for (p = elt->first_same_value; p; p = p->next_same_value)
if (p->is_const)
{
if (zero_sign_extended_src)
{
rtx truncated_const, trial;
truncated_const = gen_rtx_TRUNCATE (
GET_MODE (XEXP (zero_sign_extended_src, 0)),
copy_rtx (p->exp));
if (GET_CODE (zero_sign_extended_src) == ZERO_EXTEND)
trial = gen_rtx_ZERO_EXTEND (
GET_MODE (zero_sign_extended_src), truncated_const);
else
trial = gen_rtx_SIGN_EXTEND (
GET_MODE (zero_sign_extended_src), truncated_const);
trial = fold_rtx (trial, NULL_RTX);
if (!rtx_equal_p (trial, p->exp))
continue;
}
src_const = p->exp;
src_const_elt = elt;
break;
}
if (src_const == 0
&& (CONSTANT_P (src_folded)
|| (GET_CODE (src_folded) == MINUS
&& GET_CODE (XEXP (src_folded, 0)) == LABEL_REF
&& GET_CODE (XEXP (src_folded, 1)) == LABEL_REF)))
src_const = src_folded, src_const_elt = elt;
else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here))
src_const = src_eqv_here, src_const_elt = src_eqv_elt;
if (src_const && src_const_elt == 0)
{
sets[i].src_const_hash = HASH (src_const, mode);
src_const_elt = lookup (src_const, sets[i].src_const_hash, mode);
}
sets[i].src_const = src_const;
sets[i].src_const_elt = src_const_elt;
if (src_const_elt && elt
&& src_const_elt->first_same_value != elt->first_same_value)
merge_equiv_classes (elt, src_const_elt);
else if (src_const_elt && elt == 0)
elt = src_const_elt;
if (src_const
&& (GET_CODE (src_const) == CONST
|| (src_const_elt && src_const_elt->related_value != 0)))
{
src_related = use_related_value (src_const, src_const_elt);
if (src_related)
{
struct table_elt *src_related_elt
= lookup (src_related, HASH (src_related, mode), mode);
if (src_related_elt && elt)
{
if (elt->first_same_value
!= src_related_elt->first_same_value)
merge_equiv_classes (elt, src_related_elt);
src_related = 0;
src_related_elt = 0;
}
else if (src_related_elt && elt == 0)
elt = src_related_elt;
}
}
if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT
&& GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_BITSIZE (mode) < BITS_PER_WORD)
{
enum machine_mode wider_mode;
for (wider_mode = GET_MODE_WIDER_MODE (mode);
GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
&& src_related == 0;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
struct table_elt *const_elt
= lookup (src_const, HASH (src_const, wider_mode), wider_mode);
if (const_elt == 0)
continue;
for (const_elt = const_elt->first_same_value;
const_elt; const_elt = const_elt->next_same_value)
if (REG_P (const_elt->exp))
{
src_related = gen_lowpart (mode,
const_elt->exp);
break;
}
}
}
if (flag_expensive_optimizations && ! src_related
&& GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
{
enum machine_mode tmode;
rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
for (tmode = GET_MODE_WIDER_MODE (mode);
GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
tmode = GET_MODE_WIDER_MODE (tmode))
{
rtx inner = gen_lowpart (tmode, XEXP (src, 0));
struct table_elt *larger_elt;
if (inner)
{
PUT_MODE (new_and, tmode);
XEXP (new_and, 0) = inner;
larger_elt = lookup (new_and, HASH (new_and, tmode), tmode);
if (larger_elt == 0)
continue;
for (larger_elt = larger_elt->first_same_value;
larger_elt; larger_elt = larger_elt->next_same_value)
if (REG_P (larger_elt->exp))
{
src_related
= gen_lowpart (mode, larger_elt->exp);
break;
}
if (src_related)
break;
}
}
}
#ifdef LOAD_EXTEND_OP
if (flag_expensive_optimizations && src_related == 0
&& (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
&& GET_MODE_CLASS (mode) == MODE_INT
&& MEM_P (src) && ! do_not_record
&& LOAD_EXTEND_OP (mode) != UNKNOWN)
{
struct rtx_def memory_extend_buf;
rtx memory_extend_rtx = &memory_extend_buf;
enum machine_mode tmode;
memset (memory_extend_rtx, 0, sizeof(*memory_extend_rtx));
PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
XEXP (memory_extend_rtx, 0) = src;
for (tmode = GET_MODE_WIDER_MODE (mode);
GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
tmode = GET_MODE_WIDER_MODE (tmode))
{
struct table_elt *larger_elt;
PUT_MODE (memory_extend_rtx, tmode);
larger_elt = lookup (memory_extend_rtx,
HASH (memory_extend_rtx, tmode), tmode);
if (larger_elt == 0)
continue;
for (larger_elt = larger_elt->first_same_value;
larger_elt; larger_elt = larger_elt->next_same_value)
if (REG_P (larger_elt->exp))
{
src_related = gen_lowpart (mode,
larger_elt->exp);
break;
}
if (src_related)
break;
}
}
#endif
if (src == src_folded)
src_folded = 0;
if (elt)
elt = elt->first_same_value;
for (p = elt; p; p = p->next_same_value)
{
enum rtx_code code = GET_CODE (p->exp);
if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, false))
continue;
if (code == SUBREG
&& (GET_MODE_SIZE (GET_MODE (p->exp))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))
&& ! (src != 0
&& GET_CODE (src) == SUBREG
&& GET_MODE (src) == GET_MODE (p->exp)
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))))
continue;
if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
src = 0;
else if (src_folded && GET_CODE (src_folded) == code
&& rtx_equal_p (src_folded, p->exp))
src_folded = 0;
else if (src_eqv_here && GET_CODE (src_eqv_here) == code
&& rtx_equal_p (src_eqv_here, p->exp))
src_eqv_here = 0;
else if (src_related && GET_CODE (src_related) == code
&& rtx_equal_p (src_related, p->exp))
src_related = 0;
if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest))
src_related = dest;
}
if (src)
{
if (rtx_equal_p (src, dest))
src_cost = src_regcost = -1;
else
{
src_cost = COST (src);
src_regcost = approx_reg_cost (src);
}
}
if (src_eqv_here)
{
if (rtx_equal_p (src_eqv_here, dest))
src_eqv_cost = src_eqv_regcost = -1;
else
{
src_eqv_cost = COST (src_eqv_here);
src_eqv_regcost = approx_reg_cost (src_eqv_here);
}
}
if (src_folded)
{
if (rtx_equal_p (src_folded, dest))
src_folded_cost = src_folded_regcost = -1;
else
{
src_folded_cost = COST (src_folded);
src_folded_regcost = approx_reg_cost (src_folded);
}
}
if (src_related)
{
if (rtx_equal_p (src_related, dest))
src_related_cost = src_related_regcost = -1;
else
{
src_related_cost = COST (src_related);
src_related_regcost = approx_reg_cost (src_related);
}
}
if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF)
src_folded = src_const, src_folded_cost = src_folded_regcost = -1;
while (1)
{
rtx trial;
while (elt && !REG_P (elt->exp)
&& ! exp_equiv_p (elt->exp, elt->exp, 1, false))
elt = elt->next_same_value;
if (elt != 0
&& GET_CODE (elt->exp) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (elt->exp))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))
&& ! (src != 0
&& GET_CODE (src) == SUBREG
&& GET_MODE (src) == GET_MODE (elt->exp)
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))))
{
elt = elt->next_same_value;
continue;
}
if (elt)
{
src_elt_cost = elt->cost;
src_elt_regcost = elt->regcost;
}
if (src_folded
&& preferable (src_folded_cost, src_folded_regcost,
src_cost, src_regcost) <= 0
&& preferable (src_folded_cost, src_folded_regcost,
src_eqv_cost, src_eqv_regcost) <= 0
&& preferable (src_folded_cost, src_folded_regcost,
src_related_cost, src_related_regcost) <= 0
&& preferable (src_folded_cost, src_folded_regcost,
src_elt_cost, src_elt_regcost) <= 0)
{
trial = src_folded, src_folded_cost = MAX_COST;
if (src_folded_force_flag)
{
rtx forced = force_const_mem (mode, trial);
if (forced)
trial = forced;
}
}
else if (src
&& preferable (src_cost, src_regcost,
src_eqv_cost, src_eqv_regcost) <= 0
&& preferable (src_cost, src_regcost,
src_related_cost, src_related_regcost) <= 0
&& preferable (src_cost, src_regcost,
src_elt_cost, src_elt_regcost) <= 0)
trial = src, src_cost = MAX_COST;
else if (src_eqv_here
&& preferable (src_eqv_cost, src_eqv_regcost,
src_related_cost, src_related_regcost) <= 0
&& preferable (src_eqv_cost, src_eqv_regcost,
src_elt_cost, src_elt_regcost) <= 0)
trial = copy_rtx (src_eqv_here), src_eqv_cost = MAX_COST;
else if (src_related
&& preferable (src_related_cost, src_related_regcost,
src_elt_cost, src_elt_regcost) <= 0)
trial = copy_rtx (src_related), src_related_cost = MAX_COST;
else if (zero_sign_extended_src)
{
rtx truncated_const;
if (elt->is_const)
truncated_const = gen_rtx_TRUNCATE (
GET_MODE (XEXP (zero_sign_extended_src, 0)),
copy_rtx (elt->exp));
else
truncated_const= copy_rtx (elt->exp);
trial = GET_CODE(zero_sign_extended_src) == ZERO_EXTEND
? gen_rtx_ZERO_EXTEND (GET_MODE(zero_sign_extended_src),
truncated_const)
: gen_rtx_SIGN_EXTEND (GET_MODE(zero_sign_extended_src),
truncated_const);
trial = fold_rtx (trial, NULL_RTX);
elt = elt->next_same_value;
src_elt_cost = MAX_COST;
}
else
{
trial = copy_rtx (elt->exp);
elt = elt->next_same_value;
src_elt_cost = MAX_COST;
}
if (n_sets == 1 && dest == pc_rtx
&& (trial == pc_rtx
|| (GET_CODE (trial) == LABEL_REF
&& ! condjump_p (insn))))
{
if (GET_CODE (trial) == LABEL_REF
&& LABEL_REF_NONLOCAL_P (trial))
continue;
SET_SRC (sets[i].rtl) = trial;
cse_jumps_altered = 1;
break;
}
else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0))
{
rtx new = canon_reg (SET_SRC (sets[i].rtl), insn);
if (libcall_insn
&& (REG_P (sets[i].orig_src)
|| GET_CODE (sets[i].orig_src) == SUBREG
|| MEM_P (sets[i].orig_src)))
{
rtx note = find_reg_equal_equiv_note (libcall_insn);
if (note != 0)
XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0),
sets[i].orig_src,
copy_rtx (new));
}
validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
apply_change_group ();
break;
}
else if (constant_pool_entries_cost
&& CONSTANT_P (trial)
&& ! (GET_CODE (trial) == CONST
&& GET_CODE (XEXP (trial, 0)) == TRUNCATE)
&& ! (GET_CODE (trial) == CONST
&& GET_CODE (XEXP (trial, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (trial, 0), 0)) == LABEL_REF
&& GET_CODE (XEXP (XEXP (trial, 0), 1)) == LABEL_REF)
&& (src_folded == 0
|| (!MEM_P (src_folded)
&& ! src_folded_force_flag))
&& GET_MODE_CLASS (mode) != MODE_CC
&& mode != VOIDmode)
{
src_folded_force_flag = 1;
src_folded = trial;
src_folded_cost = constant_pool_entries_cost;
src_folded_regcost = constant_pool_entries_regcost;
}
}
src = SET_SRC (sets[i].rtl);
if (zero_sign_extended_src
&& (GET_CODE (src) == GET_CODE (zero_sign_extended_src)))
src = XEXP (src, 0);
if (REG_P (dest)
&& REGNO_QTY_VALID_P (REGNO (dest)))
{
int dest_q = REG_QTY (REGNO (dest));
struct qty_table_elem *dest_ent = &qty_table[dest_q];
if (dest_ent->mode == GET_MODE (dest)
&& dest_ent->first_reg != REGNO (dest)
&& REG_P (src) && REGNO (src) == REGNO (dest)
&& (!REG_P (sets[i].src)
|| REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER)
&& (!REG_P (dest) || REGNO (dest) >= FIRST_PSEUDO_REGISTER))
{
int src_q = REG_QTY (REGNO (src));
struct qty_table_elem *src_ent = &qty_table[src_q];
int first = src_ent->first_reg;
rtx new_src
= (first >= FIRST_PSEUDO_REGISTER
? regno_reg_rtx[first] : gen_rtx_REG (GET_MODE (src), first));
if (validate_change (insn, &SET_SRC (sets[i].rtl), new_src, 0))
{
src = new_src;
if (src_const && COST (src_const) < COST (src)
&& validate_change (insn, &SET_SRC (sets[i].rtl),
src_const, 0))
src = src_const;
}
}
}
if (src != sets[i].src)
{
cse_altered = 1;
do_not_record = 0;
hash_arg_in_memory = 0;
sets[i].src = src;
sets[i].src_hash = HASH (src, mode);
sets[i].src_volatile = do_not_record;
sets[i].src_in_memory = hash_arg_in_memory;
sets[i].src_elt = lookup (src, sets[i].src_hash, mode);
}
if (n_sets == 1 && src_const && REG_P (dest)
&& !REG_P (src_const)
&& ! (GET_CODE (src_const) == CONST
&& GET_CODE (XEXP (src_const, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
&& (GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF
|| rtx_equal_p ((XEXP (XEXP (src_const, 0), 1)),
const0_rtx))))
{
if (! rtx_equal_p (src, src_const))
{
src_const = copy_rtx (src_const);
set_unique_reg_note (insn, REG_EQUAL, src_const);
}
}
do_not_record = 0;
while (GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
sets[i].inner_dest = dest;
if (MEM_P (dest))
{
#ifdef PUSH_ROUNDING
rtx addr = XEXP (dest, 0);
if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC
&& XEXP (addr, 0) == stack_pointer_rtx)
invalidate (stack_pointer_rtx, Pmode);
#endif
dest = fold_rtx (dest, insn);
}
sets[i].dest_hash = HASH (dest, mode);
if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT)
{
rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
if (src_const != 0 && GET_CODE (src_const) == CONST_INT
&& GET_CODE (width) == CONST_INT
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& ! (INTVAL (src_const)
& ((HOST_WIDE_INT) (-1) << INTVAL (width))))
;
else
{
sets[i].src_elt = 0;
sets[i].src_volatile = 1;
src_eqv = 0;
src_eqv_elt = 0;
}
}
else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
{
delete_insn (insn);
cse_jumps_altered = 1;
sets[i].rtl = 0;
}
else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF
&& !LABEL_REF_NONLOCAL_P (src))
{
if (NEXT_INSN (insn) == 0
|| !BARRIER_P (NEXT_INSN (insn)))
emit_barrier_after (insn);
if (n_sets == 1)
{
rtx new, note;
new = emit_jump_insn_after (gen_jump (XEXP (src, 0)), insn);
JUMP_LABEL (new) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
note = find_reg_note (insn, REG_NON_LOCAL_GOTO, 0);
if (note)
{
XEXP (note, 1) = NULL_RTX;
REG_NOTES (new) = note;
}
delete_insn (insn);
insn = new;
if (NEXT_INSN (insn) == 0
|| !BARRIER_P (NEXT_INSN (insn)))
emit_barrier_after (insn);
}
else
INSN_CODE (insn) = -1;
cse_jumps_altered = 1;
sets[i].rtl = 0;
}
else if (do_not_record)
{
if (REG_P (dest) || GET_CODE (dest) == SUBREG)
invalidate (dest, VOIDmode);
else if (MEM_P (dest))
invalidate (dest, VOIDmode);
else if (GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
sets[i].rtl = 0;
}
if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
#ifdef HAVE_cc0
if (dest == cc0_rtx)
{
this_insn_cc0 = src_const && mode != VOIDmode ? src_const : src;
this_insn_cc0_mode = mode;
if (FLOAT_MODE_P (mode))
this_insn_cc0 = gen_rtx_COMPARE (VOIDmode, this_insn_cc0,
CONST0_RTX (mode));
}
#endif
}
if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile
&& ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl)))
{
struct table_elt *elt;
struct table_elt *classp = sets[0].src_elt;
rtx dest = SET_DEST (sets[0].rtl);
enum machine_mode eqvmode = GET_MODE (dest);
if (GET_CODE (dest) == STRICT_LOW_PART)
{
eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
classp = 0;
}
if (insert_regs (src_eqv, classp, 0))
{
rehash_using_reg (src_eqv);
src_eqv_hash = HASH (src_eqv, eqvmode);
}
elt = insert (src_eqv, classp, src_eqv_hash, eqvmode);
elt->in_memory = src_eqv_in_memory;
src_eqv_elt = elt;
for (i = 0; i < n_sets; i++)
if (sets[i].rtl && sets[i].src_elt == 0
&& rtx_equal_p (SET_SRC (sets[i].rtl), src_eqv))
sets[i].src_elt = src_eqv_elt;
}
for (i = 0; i < n_sets; i++)
if (sets[i].rtl && ! sets[i].src_volatile
&& ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl)))
{
if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART)
{
sets[i].src_elt = src_eqv_elt;
sets[i].src_hash = src_eqv_hash;
}
else
{
struct table_elt *classp = src_eqv_elt;
rtx src = sets[i].src;
rtx dest = SET_DEST (sets[i].rtl);
enum machine_mode mode
= GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
if (!classp)
classp = sets[i].src_const_elt;
if (sets[i].src_elt == 0)
{
if (! find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
struct table_elt *elt;
if (insert_regs (src, classp, 0))
{
rehash_using_reg (src);
sets[i].src_hash = HASH (src, mode);
}
elt = insert (src, classp, sets[i].src_hash, mode);
elt->in_memory = sets[i].src_in_memory;
sets[i].src_elt = classp = elt;
}
else
sets[i].src_elt = classp;
}
if (sets[i].src_const && sets[i].src_const_elt == 0
&& src != sets[i].src_const
&& ! rtx_equal_p (sets[i].src_const, src))
sets[i].src_elt = insert (sets[i].src_const, classp,
sets[i].src_const_hash, mode);
}
}
else if (sets[i].src_elt == 0)
sets[i].src_elt = src_eqv_elt;
invalidate_from_clobbers (x);
if (CALL_P (insn))
{
if (! CONST_OR_PURE_CALL_P (insn))
invalidate_memory ();
invalidate_for_call ();
}
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
rtx dest = SET_DEST (sets[i].rtl);
if (REG_P (dest) || GET_CODE (dest) == SUBREG)
invalidate (dest, VOIDmode);
else if (MEM_P (dest))
invalidate (dest, VOIDmode);
else if (GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
}
if (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == ASM_OPERANDS
&& MEM_VOLATILE_P (PATTERN (insn)))
flush_hash_table ();
for (i = 0; i < n_sets; i++)
{
if (sets[i].rtl)
{
rtx x = SET_DEST (sets[i].rtl);
if (!REG_P (x))
mention_regs (x);
else
{
unsigned int regno = REGNO (x);
unsigned int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: hard_regno_nregs[regno][GET_MODE (x)]);
unsigned int i;
for (i = regno; i < endregno; i++)
{
if (REG_IN_TABLE (i) >= 0)
{
remove_invalid_refs (i);
REG_IN_TABLE (i) = -1;
}
}
}
}
}
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0)
{
struct table_elt *elt = sets[i].src_elt;
while (elt && elt->prev_same_value)
elt = elt->prev_same_value;
while (elt && elt->first_same_value == 0)
elt = elt->next_same_value;
sets[i].src_elt = elt ? elt->first_same_value : 0;
}
}
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
rtx dest = SET_DEST (sets[i].rtl);
struct table_elt *elt;
if ((flag_float_store
&& MEM_P (dest)
&& FLOAT_MODE_P (GET_MODE (dest)))
|| GET_MODE (dest) == BLKmode
|| libcall_insn
|| sets[i].src_elt == 0
|| (GET_CODE (dest) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (dest))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
&& (GET_CODE (sets[i].src) == SIGN_EXTEND
|| GET_CODE (sets[i].src) == ZERO_EXTEND)))
continue;
if (GET_CODE (dest) == STRICT_LOW_PART)
dest = SUBREG_REG (XEXP (dest, 0));
if (REG_P (dest) || GET_CODE (dest) == SUBREG)
if (insert_regs (dest, sets[i].src_elt, 1))
{
rehash_using_reg (dest);
sets[i].dest_hash = HASH (dest, GET_MODE (dest));
}
elt = insert (dest, sets[i].src_elt,
sets[i].dest_hash, GET_MODE (dest));
elt->in_memory = (MEM_P (sets[i].inner_dest)
&& !MEM_READONLY_P (sets[i].inner_dest));
if (GET_CODE (dest) == SUBREG
&& (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
/ UNITS_PER_WORD)
== (GET_MODE_SIZE (GET_MODE (dest)) - 1) / UNITS_PER_WORD)
&& (GET_MODE_SIZE (GET_MODE (dest))
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
&& sets[i].src_elt != 0)
{
enum machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
struct table_elt *elt, *classp = 0;
for (elt = sets[i].src_elt->first_same_value; elt;
elt = elt->next_same_value)
{
rtx new_src = 0;
unsigned src_hash;
struct table_elt *src_elt;
int byte = 0;
if (!REG_P (elt->exp)
&& ! exp_equiv_p (elt->exp, elt->exp, 1, false))
continue;
if (GET_MODE (elt->exp) == new_mode)
new_src = elt->exp;
else
{
if (BYTES_BIG_ENDIAN)
byte = (GET_MODE_SIZE (GET_MODE (dest))
- GET_MODE_SIZE (new_mode));
new_src = simplify_gen_subreg (new_mode, elt->exp,
GET_MODE (dest), byte);
}
if (! new_src)
continue;
src_hash = HASH (new_src, new_mode);
src_elt = lookup (new_src, src_hash, new_mode);
if (src_elt == 0)
{
if (insert_regs (new_src, classp, 0))
{
rehash_using_reg (new_src);
src_hash = HASH (new_src, new_mode);
}
src_elt = insert (new_src, classp, src_hash, new_mode);
src_elt->in_memory = elt->in_memory;
}
else if (classp && classp != src_elt->first_same_value)
merge_equiv_classes (src_elt, classp);
classp = src_elt->first_same_value;
while (classp
&& !REG_P (classp->exp)
&& ! exp_equiv_p (classp->exp, classp->exp, 1, false))
classp = classp->next_same_value;
}
}
}
if (n_sets == 1 && sets[0].rtl && REG_P (SET_DEST (sets[0].rtl))
&& NEXT_INSN (PREV_INSN (insn)) == insn
&& REG_P (SET_SRC (sets[0].rtl))
&& REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER
&& REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl))))
{
int src_q = REG_QTY (REGNO (SET_SRC (sets[0].rtl)));
struct qty_table_elem *src_ent = &qty_table[src_q];
if ((src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl)))
&& ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
rtx prev = insn;
do
{
prev = PREV_INSN (prev);
}
while (prev && NOTE_P (prev)
&& NOTE_LINE_NUMBER (prev) != NOTE_INSN_BASIC_BLOCK);
if (prev != 0 && NONJUMP_INSN_P (prev)
&& GET_CODE (PATTERN (prev)) == SET
&& SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)
&& ! find_reg_note (prev, REG_EQUIV, NULL_RTX))
{
rtx dest = SET_DEST (sets[0].rtl);
rtx src = SET_SRC (sets[0].rtl);
rtx note;
validate_change (prev, &SET_DEST (PATTERN (prev)), dest, 1);
validate_change (insn, &SET_DEST (sets[0].rtl), src, 1);
validate_change (insn, &SET_SRC (sets[0].rtl), dest, 1);
apply_change_group ();
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (note != 0
&& (reg_mentioned_p (dest, XEXP (note, 0))
|| rtx_equal_p (src, XEXP (note, 0))))
remove_note (insn, note);
}
}
}
if (JUMP_P (insn)
&& n_sets == 1 && GET_CODE (x) == SET
&& GET_CODE (SET_SRC (x)) == IF_THEN_ELSE)
record_jump_equiv (insn, 0);
#ifdef HAVE_cc0
if (prev_insn && NONJUMP_INSN_P (prev_insn)
&& (tem = single_set (prev_insn)) != 0
&& SET_DEST (tem) == cc0_rtx
&& ! reg_mentioned_p (cc0_rtx, x))
delete_insn (prev_insn);
prev_insn_cc0 = this_insn_cc0;
prev_insn_cc0_mode = this_insn_cc0_mode;
prev_insn = insn;
#endif
}
static void
invalidate_memory (void)
{
int i;
struct table_elt *p, *next;
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory)
remove_from_table (p, i);
}
}
static int
addr_affects_sp_p (rtx addr)
{
if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC
&& REG_P (XEXP (addr, 0))
&& REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
{
if (REG_TICK (STACK_POINTER_REGNUM) >= 0)
{
REG_TICK (STACK_POINTER_REGNUM)++;
SUBREG_TICKED (STACK_POINTER_REGNUM) = -1;
}
if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
invalidate (stack_pointer_rtx, VOIDmode);
return 1;
}
return 0;
}
static void
invalidate_from_clobbers (rtx x)
{
if (GET_CODE (x) == CLOBBER)
{
rtx ref = XEXP (x, 0);
if (ref)
{
if (REG_P (ref) || GET_CODE (ref) == SUBREG
|| MEM_P (ref))
invalidate (ref, VOIDmode);
else if (GET_CODE (ref) == STRICT_LOW_PART
|| GET_CODE (ref) == ZERO_EXTRACT)
invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
else if (GET_CODE (x) == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
rtx y = XVECEXP (x, 0, i);
if (GET_CODE (y) == CLOBBER)
{
rtx ref = XEXP (y, 0);
if (REG_P (ref) || GET_CODE (ref) == SUBREG
|| MEM_P (ref))
invalidate (ref, VOIDmode);
else if (GET_CODE (ref) == STRICT_LOW_PART
|| GET_CODE (ref) == ZERO_EXTRACT)
invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
}
}
static rtx
cse_process_notes (rtx x, rtx object)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
int i;
switch (code)
{
case CONST_INT:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
case CONST_VECTOR:
case PC:
case CC0:
case LO_SUM:
return x;
case MEM:
validate_change (x, &XEXP (x, 0),
cse_process_notes (XEXP (x, 0), x), 0);
return x;
case EXPR_LIST:
case INSN_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL)
XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX);
if (XEXP (x, 1))
XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX);
return x;
case SIGN_EXTEND:
case ZERO_EXTEND:
case SUBREG:
{
rtx new = cse_process_notes (XEXP (x, 0), object);
if (GET_MODE (new) != VOIDmode)
validate_change (object, &XEXP (x, 0), new, 0);
return x;
}
case REG:
i = REG_QTY (REGNO (x));
if (REGNO_QTY_VALID_P (REGNO (x)))
{
struct qty_table_elem *ent = &qty_table[i];
if (ent->const_rtx != NULL_RTX
&& (CONSTANT_P (ent->const_rtx)
|| REG_P (ent->const_rtx)))
{
rtx new = gen_lowpart (GET_MODE (x), ent->const_rtx);
if (new)
return new;
}
}
return canon_reg (x, NULL_RTX);
default:
break;
}
for (i = 0; i < GET_RTX_LENGTH (code); i++)
if (fmt[i] == 'e')
validate_change (object, &XEXP (x, i),
cse_process_notes (XEXP (x, i), object), 0);
return x;
}
static void
invalidate_skipped_set (rtx dest, rtx set, void *data ATTRIBUTE_UNUSED)
{
enum rtx_code code = GET_CODE (dest);
if (code == MEM
&& ! addr_affects_sp_p (dest)
&& (MEM_IN_STRUCT_P (dest) || GET_MODE (dest) == BLKmode
|| cse_rtx_varies_p (XEXP (dest, 0), 0)))
{
invalidate_memory ();
return;
}
if (GET_CODE (set) == CLOBBER
|| CC0_P (dest)
|| dest == pc_rtx)
return;
if (code == STRICT_LOW_PART || code == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
else if (code == REG || code == SUBREG || code == MEM)
invalidate (dest, VOIDmode);
}
static void
invalidate_skipped_block (rtx start)
{
rtx insn;
for (insn = start; insn && !LABEL_P (insn);
insn = NEXT_INSN (insn))
{
if (! INSN_P (insn))
continue;
if (CALL_P (insn))
{
if (! CONST_OR_PURE_CALL_P (insn))
invalidate_memory ();
invalidate_for_call ();
}
invalidate_from_clobbers (PATTERN (insn));
note_stores (PATTERN (insn), invalidate_skipped_set, NULL);
}
}
static void
cse_end_of_basic_block (rtx insn, struct cse_basic_block_data *data,
int follow_jumps, int skip_blocks)
{
rtx p = insn, q;
int nsets = 0;
int low_cuid = INSN_CUID (insn), high_cuid = INSN_CUID (insn);
rtx next = INSN_P (insn) ? insn : next_real_insn (insn);
int path_size = data->path_size;
int path_entry = 0;
int i;
while (path_size > 0)
{
if (data->path[path_size - 1].status != PATH_NOT_TAKEN)
{
data->path[path_size - 1].status = PATH_NOT_TAKEN;
break;
}
else
path_size--;
}
if (GET_MODE (insn) == QImode)
follow_jumps = skip_blocks = 0;
while (p && !LABEL_P (p))
{
if (PREV_INSN (p) && CALL_P (PREV_INSN (p))
&& find_reg_note (PREV_INSN (p), REG_SETJMP, NULL))
break;
if (INSN_P (p) && GET_CODE (PATTERN (p)) == PARALLEL)
nsets += XVECLEN (PATTERN (p), 0);
else if (!NOTE_P (p))
nsets += 1;
if (INSN_UID (p) <= max_uid && INSN_CUID (p) > high_cuid)
high_cuid = INSN_CUID (p);
if (INSN_UID (p) <= max_uid && INSN_CUID (p) < low_cuid)
low_cuid = INSN_CUID (p);
if (path_entry < path_size && data->path[path_entry].branch == p)
{
if (data->path[path_entry].status != PATH_NOT_TAKEN)
p = JUMP_LABEL (p);
path_entry++;
}
else if ((follow_jumps || skip_blocks) && path_size < PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH) - 1
&& JUMP_P (p)
&& GET_CODE (PATTERN (p)) == SET
&& GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE
&& JUMP_LABEL (p) != 0
&& LABEL_NUSES (JUMP_LABEL (p)) == 1
&& NEXT_INSN (JUMP_LABEL (p)) != 0)
{
for (q = PREV_INSN (JUMP_LABEL (p)); q; q = PREV_INSN (q))
if ((!NOTE_P (q)
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END
|| (PREV_INSN (q) && CALL_P (PREV_INSN (q))
&& find_reg_note (PREV_INSN (q), REG_SETJMP, NULL)))
&& (!LABEL_P (q) || LABEL_NUSES (q) != 0))
break;
if (follow_jumps && q != 0 && BARRIER_P (q))
{
if (next_real_insn (q) == next)
{
p = NEXT_INSN (p);
continue;
}
for (i = 0; i < path_entry; i++)
if (data->path[i].branch == p)
break;
if (i != path_entry)
break;
data->path[path_entry].branch = p;
data->path[path_entry++].status = PATH_TAKEN;
path_size = path_entry;
p = JUMP_LABEL (p);
PUT_MODE (NEXT_INSN (p), QImode);
}
else if (skip_blocks && q != 0 && !LABEL_P (q))
{
rtx tmp;
if (next_real_insn (q) == next)
{
p = NEXT_INSN (p);
continue;
}
for (i = 0; i < path_entry; i++)
if (data->path[i].branch == p)
break;
if (i != path_entry)
break;
for (tmp = NEXT_INSN (p); tmp && tmp != q; tmp = NEXT_INSN (tmp))
if (LABEL_P (tmp))
break;
if (tmp == q)
{
data->path[path_entry].branch = p;
data->path[path_entry++].status = PATH_AROUND;
path_size = path_entry;
p = JUMP_LABEL (p);
PUT_MODE (NEXT_INSN (p), QImode);
}
}
}
p = NEXT_INSN (p);
}
data->low_cuid = low_cuid;
data->high_cuid = high_cuid;
data->nsets = nsets;
data->last = p;
for (i = path_size - 1; i >= 0; i--)
if (data->path[i].status != PATH_NOT_TAKEN)
break;
if (i == -1)
data->path_size = 0;
else
data->path_size = path_size;
data->path[path_size].branch = 0;
}
int
cse_main (rtx f, int nregs, FILE *file)
{
struct cse_basic_block_data val;
rtx insn = f;
int i;
init_cse_reg_info (nregs);
val.path = xmalloc (sizeof (struct branch_path)
* PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
cse_jumps_altered = 0;
recorded_label_ref = 0;
constant_pool_entries_cost = 0;
constant_pool_entries_regcost = 0;
val.path_size = 0;
rtl_hooks = cse_rtl_hooks;
init_recog ();
init_alias_analysis ();
reg_eqv_table = xmalloc (nregs * sizeof (struct reg_eqv_elem));
max_uid = get_max_uid ();
uid_cuid = xcalloc (max_uid + 1, sizeof (int));
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
if (!NOTE_P (insn)
|| NOTE_LINE_NUMBER (insn) < 0)
INSN_CUID (insn) = ++i;
else
INSN_CUID (insn) = i;
}
insn = f;
while (insn)
{
cse_altered = 0;
cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps,
flag_cse_skip_blocks);
if (val.nsets == 0 || GET_MODE (insn) == QImode)
{
PUT_MODE (insn, VOIDmode);
insn = (val.last ? NEXT_INSN (val.last) : 0);
val.path_size = 0;
continue;
}
cse_basic_block_start = val.low_cuid;
cse_basic_block_end = val.high_cuid;
max_qty = val.nsets * 2;
if (file)
fnotice (file, ";; Processing block from %d to %d, %d sets.\n",
INSN_UID (insn), val.last ? INSN_UID (val.last) : 0,
val.nsets);
if (max_qty < 500)
max_qty = 500;
if (val.path_size > 0)
cse_basic_block (insn, val.last, val.path);
else
{
int old_cse_jumps_altered = cse_jumps_altered;
rtx temp;
cse_jumps_altered = 0;
temp = cse_basic_block (insn, val.last, val.path);
if (cse_jumps_altered == 0
|| (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
insn = temp;
cse_jumps_altered |= old_cse_jumps_altered;
}
if (cse_altered)
ggc_collect ();
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
end_alias_analysis ();
free (uid_cuid);
free (reg_eqv_table);
free (val.path);
rtl_hooks = general_rtl_hooks;
return cse_jumps_altered || recorded_label_ref;
}
static rtx
cse_basic_block (rtx from, rtx to, struct branch_path *next_branch)
{
rtx insn;
int to_usage = 0;
rtx libcall_insn = NULL_RTX;
int num_insns = 0;
int no_conflict = 0;
qty_table = xmalloc (max_qty * sizeof (struct qty_table_elem));
new_basic_block ();
if (to != 0 && LABEL_P (to))
++LABEL_NUSES (to);
for (insn = from; insn != to; insn = NEXT_INSN (insn))
{
enum rtx_code code = GET_CODE (insn);
if (code != NOTE && num_insns++ > 1000)
{
flush_hash_table ();
num_insns = 0;
}
if (next_branch->branch == insn)
{
enum taken status = next_branch++->status;
if (status != PATH_NOT_TAKEN)
{
if (status == PATH_TAKEN)
record_jump_equiv (insn, 1);
else
invalidate_skipped_block (NEXT_INSN (insn));
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
prev_insn = insn;
#endif
insn = JUMP_LABEL (insn);
continue;
}
}
if (GET_MODE (insn) == QImode)
PUT_MODE (insn, VOIDmode);
if (GET_RTX_CLASS (code) == RTX_INSN)
{
rtx p;
if (REG_NOTES (insn))
REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), NULL_RTX);
if (REG_NOTES (insn) != 0)
{
if ((p = find_reg_note (insn, REG_LIBCALL, NULL_RTX)))
libcall_insn = XEXP (p, 0);
else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
if (! no_conflict)
libcall_insn = 0;
else
no_conflict = -1;
}
else if (find_reg_note (insn, REG_NO_CONFLICT, NULL_RTX))
no_conflict = 1;
}
cse_insn (insn, libcall_insn);
if (no_conflict == -1)
{
libcall_insn = 0;
no_conflict = 0;
}
if (NONJUMP_INSN_P (insn) && ! recorded_label_ref
&& for_each_rtx (&PATTERN (insn), check_for_label_ref,
(void *) insn))
recorded_label_ref = 1;
}
if (any_uncondjump_p (insn))
{
if (to == 0)
{
free (qty_table);
return 0;
}
if (JUMP_LABEL (insn) == to)
to_usage = 1;
if (INSN_DELETED_P (to))
break;
insn = PREV_INSN (to);
}
if (to != 0 && NEXT_INSN (insn) == to
&& LABEL_P (to) && --LABEL_NUSES (to) == to_usage)
{
struct cse_basic_block_data val;
rtx prev;
insn = NEXT_INSN (to);
if (insn == 0)
{
free (qty_table);
return 0;
}
prev = prev_nonnote_insn (to);
if (prev && BARRIER_P (prev))
{
free (qty_table);
return insn;
}
to_usage = 0;
val.path_size = 0;
val.path = xmalloc (sizeof (struct branch_path)
* PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
cse_end_of_basic_block (insn, &val, 0, 0);
free (val.path);
if (val.nsets * 2 + next_qty > max_qty)
break;
cse_basic_block_start = val.low_cuid;
cse_basic_block_end = val.high_cuid;
to = val.last;
if (to != 0 && LABEL_P (to))
++LABEL_NUSES (to);
insn = PREV_INSN (insn);
}
}
gcc_assert (next_qty <= max_qty);
free (qty_table);
return to ? NEXT_INSN (to) : 0;
}
static int
check_for_label_ref (rtx *rtl, void *data)
{
rtx insn = (rtx) data;
return (GET_CODE (*rtl) == LABEL_REF
&& ! LABEL_REF_NONLOCAL_P (*rtl)
&& LABEL_P (XEXP (*rtl, 0))
&& INSN_UID (XEXP (*rtl, 0)) != 0
&& ! find_reg_note (insn, REG_LABEL, XEXP (*rtl, 0)));
}
static void
count_reg_usage (rtx x, int *counts, int incr)
{
enum rtx_code code;
rtx note;
const char *fmt;
int i, j;
if (x == 0)
return;
switch (code = GET_CODE (x))
{
case REG:
counts[REGNO (x)] += incr;
return;
case PC:
case CC0:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
return;
case CLOBBER:
if (MEM_P (XEXP (x, 0)))
count_reg_usage (XEXP (XEXP (x, 0), 0), counts, incr);
return;
case SET:
if (!REG_P (SET_DEST (x)))
count_reg_usage (SET_DEST (x), counts, incr);
count_reg_usage (SET_SRC (x), counts, incr);
return;
case CALL_INSN:
count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, incr);
case INSN:
case JUMP_INSN:
count_reg_usage (PATTERN (x), counts, incr);
note = find_reg_equal_equiv_note (x);
if (note)
{
rtx eqv = XEXP (note, 0);
if (GET_CODE (eqv) == EXPR_LIST)
do
{
count_reg_usage (XEXP (eqv, 0), counts, incr);
eqv = XEXP (eqv, 1);
}
while (eqv && GET_CODE (eqv) == EXPR_LIST);
else
count_reg_usage (eqv, counts, incr);
}
return;
case EXPR_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL
|| (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE)
|| GET_CODE (XEXP (x, 0)) == CLOBBER)
count_reg_usage (XEXP (x, 0), counts, incr);
count_reg_usage (XEXP (x, 1), counts, incr);
return;
case ASM_OPERANDS:
for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
count_reg_usage (ASM_OPERANDS_INPUT (x, i), counts, incr);
return;
case INSN_LIST:
gcc_unreachable ();
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
count_reg_usage (XEXP (x, i), counts, incr);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
count_reg_usage (XVECEXP (x, i, j), counts, incr);
}
}
static bool
set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED,
int *counts)
{
#ifdef HAVE_cc0
rtx tem;
#endif
if (set_noop_p (set))
;
#ifdef HAVE_cc0
else if (GET_CODE (SET_DEST (set)) == CC0
&& !side_effects_p (SET_SRC (set))
&& ((tem = next_nonnote_insn (insn)) == 0
|| !INSN_P (tem)
|| !reg_referenced_p (cc0_rtx, PATTERN (tem))))
return false;
#endif
else if (!REG_P (SET_DEST (set))
|| REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
|| counts[REGNO (SET_DEST (set))] != 0
|| side_effects_p (SET_SRC (set)))
return true;
return false;
}
static bool
insn_live_p (rtx insn, int *counts)
{
int i;
if (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))
return true;
else if (GET_CODE (PATTERN (insn)) == SET)
return set_live_p (PATTERN (insn), insn, counts);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
{
rtx elt = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (elt) == SET)
{
if (set_live_p (elt, insn, counts))
return true;
}
else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
return true;
}
return false;
}
else
return true;
}
static bool
dead_libcall_p (rtx insn, int *counts)
{
rtx note, set, new;
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (!note)
return false;
set = single_set (insn);
if (!set)
return false;
new = simplify_rtx (XEXP (note, 0));
if (!new)
new = XEXP (note, 0);
count_reg_usage (insn, counts, -1);
if (validate_change (insn, &SET_SRC (set), new, 0))
{
count_reg_usage (insn, counts, 1);
remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
remove_note (insn, note);
return true;
}
if (CONSTANT_P (new))
{
new = force_const_mem (GET_MODE (SET_DEST (set)), new);
if (new && validate_change (insn, &SET_SRC (set), new, 0))
{
count_reg_usage (insn, counts, 1);
remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
remove_note (insn, note);
return true;
}
}
count_reg_usage (insn, counts, 1);
return false;
}
int
delete_trivially_dead_insns (rtx insns, int nreg)
{
int *counts;
rtx insn, prev;
int in_libcall = 0, dead_libcall = 0;
int ndead = 0;
timevar_push (TV_DELETE_TRIVIALLY_DEAD);
counts = xcalloc (nreg, sizeof (int));
for (insn = insns; insn; insn = NEXT_INSN (insn))
if (INSN_P (insn))
count_reg_usage (insn, counts, 1);
for (insn = get_last_insn (); insn; insn = prev)
{
int live_insn = 0;
prev = PREV_INSN (insn);
if (!INSN_P (insn))
continue;
if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
in_libcall = 1;
live_insn = 1;
dead_libcall = dead_libcall_p (insn, counts);
}
else if (in_libcall)
live_insn = ! dead_libcall;
else
live_insn = insn_live_p (insn, counts);
if (! live_insn)
{
count_reg_usage (insn, counts, -1);
delete_insn_and_edges (insn);
ndead++;
}
if (in_libcall && find_reg_note (insn, REG_LIBCALL, NULL_RTX))
{
in_libcall = 0;
dead_libcall = 0;
}
}
if (dump_file && ndead)
fprintf (dump_file, "Deleted %i trivially dead insns\n",
ndead);
free (counts);
timevar_pop (TV_DELETE_TRIVIALLY_DEAD);
return ndead;
}
static int
cse_change_cc_mode (rtx *loc, void *data)
{
struct change_cc_mode_args* args = (struct change_cc_mode_args*)data;
if (*loc
&& REG_P (*loc)
&& REGNO (*loc) == REGNO (args->newreg)
&& GET_MODE (*loc) != GET_MODE (args->newreg))
{
validate_change (args->insn, loc, args->newreg, 1);
return -1;
}
return 0;
}
static void
cse_change_cc_mode_insn (rtx insn, rtx newreg)
{
struct change_cc_mode_args args;
int success;
if (!INSN_P (insn))
return;
args.insn = insn;
args.newreg = newreg;
for_each_rtx (&PATTERN (insn), cse_change_cc_mode, &args);
for_each_rtx (®_NOTES (insn), cse_change_cc_mode, &args);
success = apply_change_group ();
gcc_assert (success);
}
static void
cse_change_cc_mode_insns (rtx start, rtx end, rtx newreg)
{
rtx insn;
for (insn = start; insn != end; insn = NEXT_INSN (insn))
{
if (! INSN_P (insn))
continue;
if (reg_set_p (newreg, insn))
return;
cse_change_cc_mode_insn (insn, newreg);
}
}
static enum machine_mode
cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode)
{
bool found_equiv;
enum machine_mode mode;
unsigned int insn_count;
edge e;
rtx insns[2];
enum machine_mode modes[2];
rtx last_insns[2];
unsigned int i;
rtx newreg;
edge_iterator ei;
found_equiv = false;
mode = GET_MODE (cc_src);
insn_count = 0;
FOR_EACH_EDGE (e, ei, bb->succs)
{
rtx insn;
rtx end;
if (e->flags & EDGE_COMPLEX)
continue;
if (EDGE_COUNT (e->dest->preds) != 1
|| e->dest == EXIT_BLOCK_PTR)
continue;
end = NEXT_INSN (BB_END (e->dest));
for (insn = BB_HEAD (e->dest); insn != end; insn = NEXT_INSN (insn))
{
rtx set;
if (! INSN_P (insn))
continue;
if (modified_in_p (cc_src, insn))
break;
set = single_set (insn);
if (set
&& REG_P (SET_DEST (set))
&& REGNO (SET_DEST (set)) == REGNO (cc_reg))
{
bool found;
enum machine_mode set_mode;
enum machine_mode comp_mode;
found = false;
set_mode = GET_MODE (SET_SRC (set));
comp_mode = set_mode;
if (rtx_equal_p (cc_src, SET_SRC (set)))
found = true;
else if (GET_CODE (cc_src) == COMPARE
&& GET_CODE (SET_SRC (set)) == COMPARE
&& mode != set_mode
&& rtx_equal_p (XEXP (cc_src, 0),
XEXP (SET_SRC (set), 0))
&& rtx_equal_p (XEXP (cc_src, 1),
XEXP (SET_SRC (set), 1)))
{
comp_mode = targetm.cc_modes_compatible (mode, set_mode);
if (comp_mode != VOIDmode
&& (can_change_mode || comp_mode == mode))
found = true;
}
if (found)
{
found_equiv = true;
if (insn_count < ARRAY_SIZE (insns))
{
insns[insn_count] = insn;
modes[insn_count] = set_mode;
last_insns[insn_count] = end;
++insn_count;
if (mode != comp_mode)
{
gcc_assert (can_change_mode);
mode = comp_mode;
PUT_MODE (cc_src, mode);
}
}
else
{
if (set_mode != mode)
{
break;
}
delete_insn (insn);
}
continue;
}
break;
}
if (reg_set_p (cc_reg, insn))
break;
}
if (insn == end)
{
enum machine_mode submode;
submode = cse_cc_succs (e->dest, cc_reg, cc_src, false);
if (submode != VOIDmode)
{
gcc_assert (submode == mode);
found_equiv = true;
can_change_mode = false;
}
}
}
if (! found_equiv)
return VOIDmode;
newreg = NULL_RTX;
for (i = 0; i < insn_count; ++i)
{
if (modes[i] != mode)
{
if (! newreg)
{
if (GET_MODE (cc_reg) == mode)
newreg = cc_reg;
else
newreg = gen_rtx_REG (mode, REGNO (cc_reg));
}
cse_change_cc_mode_insns (NEXT_INSN (insns[i]), last_insns[i],
newreg);
}
delete_insn (insns[i]);
}
return mode;
}
void
cse_condition_code_reg (void)
{
unsigned int cc_regno_1;
unsigned int cc_regno_2;
rtx cc_reg_1;
rtx cc_reg_2;
basic_block bb;
if (! targetm.fixed_condition_code_regs (&cc_regno_1, &cc_regno_2))
return;
cc_reg_1 = gen_rtx_REG (CCmode, cc_regno_1);
if (cc_regno_2 != INVALID_REGNUM)
cc_reg_2 = gen_rtx_REG (CCmode, cc_regno_2);
else
cc_reg_2 = NULL_RTX;
FOR_EACH_BB (bb)
{
rtx last_insn;
rtx cc_reg;
rtx insn;
rtx cc_src_insn;
rtx cc_src;
enum machine_mode mode;
enum machine_mode orig_mode;
last_insn = BB_END (bb);
if (!JUMP_P (last_insn))
continue;
if (reg_referenced_p (cc_reg_1, PATTERN (last_insn)))
cc_reg = cc_reg_1;
else if (cc_reg_2 && reg_referenced_p (cc_reg_2, PATTERN (last_insn)))
cc_reg = cc_reg_2;
else
continue;
cc_src_insn = NULL_RTX;
cc_src = NULL_RTX;
for (insn = PREV_INSN (last_insn);
insn && insn != PREV_INSN (BB_HEAD (bb));
insn = PREV_INSN (insn))
{
rtx set;
if (! INSN_P (insn))
continue;
set = single_set (insn);
if (set
&& REG_P (SET_DEST (set))
&& REGNO (SET_DEST (set)) == REGNO (cc_reg))
{
cc_src_insn = insn;
cc_src = SET_SRC (set);
break;
}
else if (reg_set_p (cc_reg, insn))
break;
}
if (! cc_src_insn)
continue;
if (modified_between_p (cc_src, cc_src_insn, NEXT_INSN (last_insn)))
continue;
orig_mode = GET_MODE (cc_src);
mode = cse_cc_succs (bb, cc_reg, cc_src, true);
if (mode != VOIDmode)
{
gcc_assert (mode == GET_MODE (cc_src));
if (mode != orig_mode)
{
rtx newreg = gen_rtx_REG (mode, REGNO (cc_reg));
cse_change_cc_mode_insn (cc_src_insn, newreg);
cse_change_cc_mode_insns (NEXT_INSN (cc_src_insn),
NEXT_INSN (last_insn),
newreg);
}
}
}
}