#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.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"
static int max_reg;
static int max_insn_uid;
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 machine_mode mode;
enum rtx_code comparison_code;
};
static struct qty_table_elem *qty_table;
#ifdef HAVE_cc0
static rtx prev_insn_cc0;
static enum machine_mode prev_insn_cc0_mode;
#endif
static rtx prev_insn;
static rtx this_insn;
struct reg_eqv_elem
{
int next, prev;
};
static struct reg_eqv_elem *reg_eqv_table;
struct cse_reg_info
{
struct cse_reg_info *hash_next;
struct cse_reg_info *next;
unsigned int regno;
int reg_qty;
int reg_tick;
int reg_in_table;
unsigned int subreg_ticked;
};
static struct cse_reg_info *cse_reg_info_free_list;
static struct cse_reg_info *cse_reg_info_used_list;
static struct cse_reg_info *cse_reg_info_used_list_end;
#define REGHASH_SHIFT 7
#define REGHASH_SIZE (1 << REGHASH_SHIFT)
#define REGHASH_MASK (REGHASH_SIZE - 1)
static struct cse_reg_info *reg_hash[REGHASH_SIZE];
#define REGHASH_FN(REGNO) \
(((REGNO) ^ ((REGNO) >> REGHASH_SHIFT)) & REGHASH_MASK)
static unsigned int cached_regno;
static struct cse_reg_info *cached_cse_reg_info;
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;
#ifdef LOAD_EXTEND_OP
static rtx memory_extend_rtx;
#endif
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 machine_mode mode;
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) \
((GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \
: canon_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) \
((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM \
|| ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \
|| ((N) < FIRST_PSEUDO_REGISTER \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
#define COST(X) (GET_CODE (X) == REG ? 0 : notreg_cost (X, SET))
#define COST_IN(X,OUTER) (GET_CODE (X) == REG ? 0 : notreg_cost (X, OUTER))
#define GET_CSE_REG_INFO(N) \
(((N) == cached_regno && cached_cse_reg_info) \
? cached_cse_reg_info : get_cse_reg_info ((N)))
#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) != (int) (N))
static struct table_elt *table[HASH_SIZE];
static struct table_elt *free_element_chain;
static int n_elements_made;
static int max_elements_made;
static struct table_elt *last_jump_equiv_class;
static int constant_pool_entries_cost;
#define PATHLENGTH 10
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 {TAKEN, NOT_TAKEN, AROUND} status;
} path[PATHLENGTH];
};
#define FIXED_BASE_PLUS_P(X) \
((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
|| ((X) == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])\
|| (X) == virtual_stack_vars_rtx \
|| (X) == virtual_incoming_args_rtx \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (XEXP (X, 0) == frame_pointer_rtx \
|| XEXP (X, 0) == hard_frame_pointer_rtx \
|| ((X) == arg_pointer_rtx \
&& fixed_regs[ARG_POINTER_REGNUM]) \
|| XEXP (X, 0) == virtual_stack_vars_rtx \
|| XEXP (X, 0) == virtual_incoming_args_rtx)) \
|| GET_CODE (X) == ADDRESSOF)
#define NONZERO_BASE_PLUS_P(X) \
((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
|| (X) == virtual_stack_vars_rtx \
|| (X) == virtual_incoming_args_rtx \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (XEXP (X, 0) == frame_pointer_rtx \
|| XEXP (X, 0) == hard_frame_pointer_rtx \
|| ((X) == arg_pointer_rtx \
&& fixed_regs[ARG_POINTER_REGNUM]) \
|| XEXP (X, 0) == virtual_stack_vars_rtx \
|| XEXP (X, 0) == virtual_incoming_args_rtx)) \
|| (X) == stack_pointer_rtx \
|| (X) == virtual_stack_dynamic_rtx \
|| (X) == virtual_outgoing_args_rtx \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (XEXP (X, 0) == stack_pointer_rtx \
|| XEXP (X, 0) == virtual_stack_dynamic_rtx \
|| XEXP (X, 0) == virtual_outgoing_args_rtx)) \
|| GET_CODE (X) == ADDRESSOF)
static int notreg_cost PARAMS ((rtx, enum rtx_code));
static int approx_reg_cost_1 PARAMS ((rtx *, void *));
static int approx_reg_cost PARAMS ((rtx));
static int preferrable PARAMS ((int, int, int, int));
static void new_basic_block PARAMS ((void));
static void make_new_qty PARAMS ((unsigned int, enum machine_mode));
static void make_regs_eqv PARAMS ((unsigned int, unsigned int));
static void delete_reg_equiv PARAMS ((unsigned int));
static int mention_regs PARAMS ((rtx));
static int insert_regs PARAMS ((rtx, struct table_elt *, int));
static void remove_from_table PARAMS ((struct table_elt *, unsigned));
static struct table_elt *lookup PARAMS ((rtx, unsigned, enum machine_mode)),
*lookup_for_remove PARAMS ((rtx, unsigned, enum machine_mode));
static rtx lookup_as_function PARAMS ((rtx, enum rtx_code));
static struct table_elt *insert PARAMS ((rtx, struct table_elt *, unsigned,
enum machine_mode));
static void merge_equiv_classes PARAMS ((struct table_elt *,
struct table_elt *));
static void invalidate PARAMS ((rtx, enum machine_mode));
static int cse_rtx_varies_p PARAMS ((rtx, int));
static void remove_invalid_refs PARAMS ((unsigned int));
static void remove_invalid_subreg_refs PARAMS ((unsigned int, unsigned int,
enum machine_mode));
static void rehash_using_reg PARAMS ((rtx));
static void invalidate_memory PARAMS ((void));
static void invalidate_for_call PARAMS ((void));
static rtx use_related_value PARAMS ((rtx, struct table_elt *));
static unsigned canon_hash PARAMS ((rtx, enum machine_mode));
static unsigned canon_hash_string PARAMS ((const char *));
static unsigned safe_hash PARAMS ((rtx, enum machine_mode));
static int exp_equiv_p PARAMS ((rtx, rtx, int, int));
static rtx canon_reg PARAMS ((rtx, rtx));
static void find_best_addr PARAMS ((rtx, rtx *, enum machine_mode));
static enum rtx_code find_comparison_args PARAMS ((enum rtx_code, rtx *, rtx *,
enum machine_mode *,
enum machine_mode *));
static rtx fold_rtx PARAMS ((rtx, rtx));
static rtx equiv_constant PARAMS ((rtx));
static void record_jump_equiv PARAMS ((rtx, int));
static void record_jump_cond PARAMS ((enum rtx_code, enum machine_mode,
rtx, rtx, int));
static void cse_insn PARAMS ((rtx, rtx));
static int addr_affects_sp_p PARAMS ((rtx));
static void invalidate_from_clobbers PARAMS ((rtx));
static rtx cse_process_notes PARAMS ((rtx, rtx));
static void cse_around_loop PARAMS ((rtx));
static void invalidate_skipped_set PARAMS ((rtx, rtx, void *));
static void invalidate_skipped_block PARAMS ((rtx));
static void cse_check_loop_start PARAMS ((rtx, rtx, void *));
static void cse_set_around_loop PARAMS ((rtx, rtx, rtx));
static rtx cse_basic_block PARAMS ((rtx, rtx, struct branch_path *, int));
static void count_reg_usage PARAMS ((rtx, int *, rtx, int));
static int check_for_label_ref PARAMS ((rtx *, void *));
extern void dump_class PARAMS ((struct table_elt*));
static struct cse_reg_info * get_cse_reg_info PARAMS ((unsigned int));
static int check_dependence PARAMS ((rtx *, void *));
static void flush_hash_table PARAMS ((void));
static bool insn_live_p PARAMS ((rtx, int *));
static bool set_live_p PARAMS ((rtx, rtx, int *));
static bool dead_libcall_p PARAMS ((rtx, int *));
void
dump_class (classp)
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 (xp, data)
rtx *xp;
void *data;
{
rtx x = *xp;
int *cost_p = data;
if (x && GET_CODE (x) == REG)
{
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 (x)
rtx x;
{
int cost = 0;
if (for_each_rtx (&x, approx_reg_cost_1, (void *) &cost))
return MAX_COST;
return cost;
}
static int
preferrable (cost_a, regcost_a, cost_b, regcost_b)
int cost_a, regcost_a, cost_b, 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 (x, outer)
rtx x;
enum rtx_code outer;
{
return ((GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == REG
&& 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);
}
int
rtx_cost (x, outer_code)
rtx x;
enum rtx_code outer_code ATTRIBUTE_UNUSED;
{
int i, j;
enum rtx_code code;
const char *fmt;
int total;
if (x == 0)
return 0;
code = GET_CODE (x);
switch (code)
{
case MULT:
total = COSTS_N_INSNS (5);
break;
case DIV:
case UDIV:
case MOD:
case UMOD:
total = COSTS_N_INSNS (7);
break;
case USE:
total = 0;
break;
default:
total = COSTS_N_INSNS (1);
}
switch (code)
{
case REG:
return 0;
case SUBREG:
if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x))))
return COSTS_N_INSNS (2
+ GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD);
break;
#ifdef RTX_COSTS
RTX_COSTS (x, code, outer_code);
#endif
#ifdef CONST_COSTS
CONST_COSTS (x, code, outer_code);
#endif
default:
#ifdef DEFAULT_RTX_COSTS
DEFAULT_RTX_COSTS (x, code, outer_code);
#endif
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
total += rtx_cost (XEXP (x, i), code);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
total += rtx_cost (XVECEXP (x, i, j), code);
return total;
}
int
address_cost (x, mode)
rtx x;
enum machine_mode mode;
{
if (GET_CODE (x) == ADDRESSOF && REG_P (XEXP ((x), 0)))
return -1;
if (!memory_address_p (mode, x))
return 1000;
#ifdef ADDRESS_COST
return ADDRESS_COST (x);
#else
return rtx_cost (x, MEM);
#endif
}
int
default_address_cost (x)
rtx x;
{
return rtx_cost (x, MEM);
}
static struct cse_reg_info *
get_cse_reg_info (regno)
unsigned int regno;
{
struct cse_reg_info **hash_head = ®_hash[REGHASH_FN (regno)];
struct cse_reg_info *p;
for (p = *hash_head; p != NULL; p = p->hash_next)
if (p->regno == regno)
break;
if (p == NULL)
{
if (cse_reg_info_free_list)
{
p = cse_reg_info_free_list;
cse_reg_info_free_list = p->next;
}
else
p = (struct cse_reg_info *) xmalloc (sizeof (struct cse_reg_info));
p->hash_next = *hash_head;
*hash_head = p;
p->reg_tick = 1;
p->reg_in_table = -1;
p->subreg_ticked = -1;
p->reg_qty = regno;
p->regno = regno;
p->next = cse_reg_info_used_list;
cse_reg_info_used_list = p;
if (!cse_reg_info_used_list_end)
cse_reg_info_used_list_end = p;
}
cached_regno = regno;
cached_cse_reg_info = p;
return p;
}
static void
new_basic_block ()
{
int i;
next_qty = max_reg;
memset ((char *) reg_hash, 0, sizeof reg_hash);
if (cse_reg_info_used_list)
{
cse_reg_info_used_list_end->next = cse_reg_info_free_list;
cse_reg_info_free_list = cse_reg_info_used_list;
cse_reg_info_used_list = cse_reg_info_used_list_end = 0;
}
cached_cse_reg_info = 0;
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;
}
}
prev_insn = 0;
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
#endif
}
static void
make_new_qty (reg, mode)
unsigned int reg;
enum machine_mode mode;
{
int q;
struct qty_table_elem *ent;
struct reg_eqv_elem *eqv;
if (next_qty >= max_qty)
abort ();
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 (new, old)
unsigned int new, old;
{
unsigned int lastr, firstr;
int q = REG_QTY (old);
struct qty_table_elem *ent;
ent = &qty_table[q];
if (! REGNO_QTY_VALID_P (old))
abort ();
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 (reg)
unsigned int reg;
{
struct qty_table_elem *ent;
int q = REG_QTY (reg);
int p, n;
if (q == (int) 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;
}
static int
mention_regs (x)
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 && GET_CODE (SUBREG_REG (x)) == REG
&& 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 || GET_RTX_CLASS (code) == '<')
{
if (GET_CODE (XEXP (x, 0)) == REG
&& ! 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 (GET_CODE (XEXP (x, 1)) == REG
&& ! 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 (x, classp, modified)
rtx x;
struct table_elt *classp;
int modified;
{
if (GET_CODE (x) == REG)
{
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 (GET_CODE (classp->exp) == REG
&& GET_MODE (classp->exp) == GET_MODE (x))
{
make_regs_eqv (regno, REGNO (classp->exp));
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 && GET_CODE (SUBREG_REG (x)) == REG
&& ! 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 (elt, hash)
struct table_elt *elt;
unsigned 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 (x, hash, mode)
rtx x;
unsigned 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 && GET_CODE (x) == REG)
|| exp_equiv_p (x, p->exp, GET_CODE (x) != REG, 0)))
return p;
return 0;
}
static struct table_elt *
lookup_for_remove (x, hash, mode)
rtx x;
unsigned hash;
enum machine_mode mode;
{
struct table_elt *p;
if (GET_CODE (x) == REG)
{
unsigned int regno = REGNO (x);
for (p = table[hash]; p; p = p->next_same_hash)
if (GET_CODE (p->exp) == REG
&& 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, 0)))
return p;
}
return 0;
}
static rtx
lookup_as_function (x, code)
rtx x;
enum rtx_code code;
{
struct table_elt *p
= lookup (x, safe_hash (x, VOIDmode) & HASH_MASK, 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) & HASH_MASK, 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, 0))
return p->exp;
return 0;
}
#define CHEAPER(X, Y) \
(preferrable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
static struct table_elt *
insert (x, classp, hash, mode)
rtx x;
struct table_elt *classp;
unsigned hash;
enum machine_mode mode;
{
struct table_elt *elt;
if (GET_CODE (x) == REG && ! REGNO_QTY_VALID_P (REGNO (x)))
abort ();
if (GET_CODE (x) == REG && 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
{
n_elements_made++;
elt = (struct table_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)
|| (GET_CODE (x) == REG
&& RTX_UNCHANGING_P (x)
&& REGNO (x) >= FIRST_PSEUDO_REGISTER)
|| 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 && GET_CODE (classp->exp) == REG
&& GET_CODE (x) != REG)
{
int exp_q = REG_QTY (REGNO (classp->exp));
struct qty_table_elem *exp_ent = &qty_table[exp_q];
exp_ent->const_rtx = gen_lowpart_if_possible (exp_ent->mode, x);
exp_ent->const_insn = this_insn;
}
else if (GET_CODE (x) == REG
&& 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 && GET_CODE (p->exp) != REG)
{
int x_q = REG_QTY (REGNO (x));
struct qty_table_elem *x_ent = &qty_table[x_q];
x_ent->const_rtx
= gen_lowpart_if_possible (GET_MODE (x), p->exp);
x_ent->const_insn = this_insn;
break;
}
}
}
else if (GET_CODE (x) == REG
&& 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) & HASH_MASK;
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 (class1, class2)
struct table_elt *class1, *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 (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0))
{
hash_arg_in_memory = 0;
hash = HASH (exp, mode);
if (GET_CODE (exp) == REG)
delete_reg_equiv (REGNO (exp));
remove_from_table (elt, hash);
if (insert_regs (exp, class1, 0))
{
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 ()
{
int i;
struct table_elt *p;
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = table[i])
{
if (GET_CODE (p->exp) == REG)
invalidate (p->exp, p->mode);
else
remove_from_table (p, i);
}
}
struct check_dependence_data
{
enum machine_mode mode;
rtx exp;
};
static int
check_dependence (x, data)
rtx *x;
void *data;
{
struct check_dependence_data *d = (struct check_dependence_data *) data;
if (*x && GET_CODE (*x) == MEM)
return true_dependence (d->exp, d->mode, *x, cse_rtx_varies_p);
else
return 0;
}
static void
invalidate (x, full_mode)
rtx x;
enum machine_mode full_mode;
{
int i;
struct table_elt *p;
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 (GET_CODE (p->exp) != REG
|| 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:
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.mode = full_mode;
if (for_each_rtx (&p->canon_exp, check_dependence, &d))
remove_from_table (p, i);
}
}
}
return;
default:
abort ();
}
}
static void
remove_invalid_refs (regno)
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 (GET_CODE (p->exp) != REG
&& refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
remove_from_table (p, i);
}
}
static void
remove_invalid_subreg_refs (regno, offset, mode)
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 (GET_CODE (exp) != REG
&& (GET_CODE (exp) != SUBREG
|| GET_CODE (SUBREG_REG (exp)) != REG
|| 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 (x)
rtx x;
{
unsigned int i;
struct table_elt *p, *next;
unsigned hash;
if (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
if (GET_CODE (x) != REG
|| 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 (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp)
&& exp_equiv_p (p->exp, p->exp, 1, 0)
&& i != (hash = safe_hash (p->exp, p->mode) & HASH_MASK))
{
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 ()
{
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 (GET_CODE (p->exp) != REG
|| 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 (x, elt)
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)) & HASH_MASK,
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 (GET_CODE (q->exp) == REG)
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
canon_hash_string (ps)
const char *ps;
{
unsigned hash = 0;
const unsigned char *p = (const unsigned char *) ps;
if (p)
while (*p)
hash += *p++;
return hash;
}
static unsigned
canon_hash (x, mode)
rtx x;
enum machine_mode mode;
{
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);
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 = 1;
return 0;
}
hash += ((unsigned) REG << 7) + (unsigned) REG_QTY (regno);
return hash;
}
case SUBREG:
{
if (GET_CODE (SUBREG_REG (x)) == REG)
{
hash += (((unsigned) SUBREG << 7)
+ REGNO (SUBREG_REG (x))
+ (SUBREG_BYTE (x) / UNITS_PER_WORD));
return hash;
}
break;
}
case CONST_INT:
{
unsigned HOST_WIDE_INT tem = INTVAL (x);
hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
return hash;
}
case CONST_DOUBLE:
hash += (unsigned) code + (unsigned) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
return hash;
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 += canon_hash (elt, GET_MODE (elt));
}
return hash;
}
case LABEL_REF:
hash += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
return hash;
case SYMBOL_REF:
hash += ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0);
return hash;
case MEM:
if (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)
{
do_not_record = 1;
return 0;
}
if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
{
hash_arg_in_memory = 1;
}
hash += (unsigned) MEM;
x = XEXP (x, 0);
goto repeat;
case USE:
if (GET_CODE (XEXP (x, 0)) == MEM
&& ! MEM_VOLATILE_P (XEXP (x, 0)))
{
hash += (unsigned) USE;
x = XEXP (x, 0);
if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
hash_arg_in_memory = 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 = 1;
return 0;
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
{
do_not_record = 1;
return 0;
}
else
{
hash += (unsigned) code + (unsigned) GET_MODE (x)
+ canon_hash_string (ASM_OPERANDS_TEMPLATE (x))
+ canon_hash_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 += (canon_hash (ASM_OPERANDS_INPUT (x, i),
GET_MODE (ASM_OPERANDS_INPUT (x, i)))
+ canon_hash_string (ASM_OPERANDS_INPUT_CONSTRAINT
(x, i)));
}
hash += canon_hash_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--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
if (i == 0)
{
x = tem;
goto repeat;
}
hash += canon_hash (tem, 0);
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
hash += canon_hash (XVECEXP (x, i, j), 0);
else if (fmt[i] == 's')
hash += canon_hash_string (XSTR (x, i));
else if (fmt[i] == 'i')
{
unsigned tem = XINT (x, i);
hash += tem;
}
else if (fmt[i] == '0' || fmt[i] == 't')
;
else
abort ();
}
return hash;
}
static unsigned
safe_hash (x, mode)
rtx x;
enum machine_mode mode;
{
int save_do_not_record = do_not_record;
int save_hash_arg_in_memory = hash_arg_in_memory;
unsigned hash = canon_hash (x, mode);
hash_arg_in_memory = save_hash_arg_in_memory;
do_not_record = save_do_not_record;
return hash;
}
static int
exp_equiv_p (x, y, validate, equal_values)
rtx x, y;
int validate;
int equal_values;
{
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))
{
if (!equal_values)
return 0;
if (CONSTANT_P (x) && GET_CODE (y) == REG
&& REGNO_QTY_VALID_P (REGNO (y)))
{
int y_q = REG_QTY (REGNO (y));
struct qty_table_elem *y_ent = &qty_table[y_q];
if (GET_MODE (y) == y_ent->mode
&& rtx_equal_p (x, y_ent->const_rtx)
&& (! validate || REG_IN_TABLE (REGNO (y)) == REG_TICK (REGNO (y))))
return 1;
}
if (CONSTANT_P (y) && code == REG
&& 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
&& rtx_equal_p (y, x_ent->const_rtx))
return 1;
}
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:
{
unsigned int regno = REGNO (y);
unsigned int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (y)));
unsigned int i;
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 PLUS:
case MULT:
case AND:
case IOR:
case XOR:
case NE:
case EQ:
return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
validate, equal_values))
|| (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
validate, equal_values)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
validate, equal_values)));
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, equal_values)
|| 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, equal_values))
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, equal_values))
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:
abort ();
}
}
return 1;
}
static int
cse_rtx_varies_p (x, from_alias)
rtx x;
int from_alias;
{
if (GET_CODE (x) == REG
&& 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
&& GET_CODE (XEXP (x, 0)) == REG
&& 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
&& GET_CODE (XEXP (x, 0)) == REG
&& GET_CODE (XEXP (x, 1)) == REG
&& 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 rtx
canon_reg (x, insn)
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')
{
rtx new = canon_reg (XEXP (x, i), insn);
int insn_code;
if (insn != 0 && new != 0
&& GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG
&& (((REGNO (new) < FIRST_PSEUDO_REGISTER)
!= (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER))
|| (insn_code = recog_memoized (insn)) < 0
|| insn_data[insn_code].n_dups > 0))
validate_change (insn, &XEXP (x, i), new, 1);
else
XEXP (x, i) = new;
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j), insn);
}
return x;
}
static void
find_best_addr (insn, loc, mode)
rtx insn;
rtx *loc;
enum machine_mode mode;
{
struct table_elt *elt;
rtx addr = *loc;
#ifdef ADDRESS_COST
struct table_elt *p;
int found_better = 1;
#endif
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
&& GET_CODE (XEXP (addr, 0)) == REG
&& 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))
|| (GET_CODE (addr) == REG
&& (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
|| regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
|| GET_CODE (addr) == ADDRESSOF
|| CONSTANT_ADDRESS_P (addr))
return;
if (GET_CODE (addr) != REG)
{
rtx folded = fold_rtx (copy_rtx (addr), NULL_RTX);
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);
#ifndef ADDRESS_COST
if (elt)
{
int our_cost = elt->cost;
for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
if (elt->cost < our_cost
&& (GET_CODE (elt->exp) == REG
|| exp_equiv_p (elt->exp, elt->exp, 1, 0))
&& validate_change (insn, loc,
canon_reg (copy_rtx (elt->exp), NULL_RTX), 0))
return;
}
#else
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 ((GET_CODE (p->exp) == REG
|| exp_equiv_p (p->exp, p->exp, 1, 0))
&& ((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
&& (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
|| GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
&& GET_CODE (XEXP (*loc, 0)) == REG
&& GET_CODE (XEXP (*loc, 1)) == CONST_INT)
{
rtx c = 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
&& (GET_CODE (p->exp) == REG
|| exp_equiv_p (p->exp, p->exp, 1, 0)))
{
rtx new = simplify_gen_binary (GET_CODE (*loc), Pmode,
p->exp, c);
int new_cost;
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;
}
}
}
#endif
}
static enum rtx_code
find_comparison_args (code, parg1, parg2, pmode1, pmode2)
enum rtx_code code;
rtx *parg1, *parg2;
enum machine_mode *pmode1, *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 (GET_RTX_CLASS (GET_CODE (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)) & HASH_MASK,
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, 0))
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
)
&& GET_RTX_CLASS (GET_CODE (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
)
&& GET_RTX_CLASS (GET_CODE (p->exp)) == '<')
{
reverse_code = 1;
x = p->exp;
break;
}
else if (NONZERO_BASE_PLUS_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 (GET_RTX_CLASS (GET_CODE (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 (x, insn)
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 EXPR_LIST:
case ADDRESSOF:
return x;
#ifdef HAVE_cc0
case CC0:
return prev_insn_cc0;
#endif
case PC:
if (insn && GET_CODE (insn) == JUMP_INSN)
{
rtx next = next_nonnote_insn (insn);
if (next && GET_CODE (next) == CODE_LABEL
&& NEXT_INSN (next) != 0
&& GET_CODE (NEXT_INSN (next)) == JUMP_INSN
&& (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC
|| GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC))
return gen_rtx_LABEL_REF (Pmode, next);
}
break;
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, 0))
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 (GET_CODE (folded_arg0) == REG
&& GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0))
&& subreg_lowpart_p (x))
{
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;
for (; elt; elt = elt->next_same_value)
{
enum rtx_code eltcode = GET_CODE (elt->exp);
if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1'
&& GET_CODE (elt->exp) != SIGN_EXTEND
&& GET_CODE (elt->exp) != 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 (GET_CODE (op0) != REG && ! 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 ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2'
|| GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c')
&& 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 && GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
if (op0)
op0 = equiv_constant (op0);
if (op1 && GET_CODE (op1) != REG && ! 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;
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, 0))
new = copy_rtx (SUBREG_REG (elt->exp));
if (new)
return new;
}
}
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;
#ifdef TARGET_FUSED_MADD
case PLUS:
new = lookup_as_function (XEXP (x, 0), NEG);
if (new)
{
rtx new2 = lookup_as_function (XEXP (new, 0), NEG);
if (!new2)
return fold_rtx (gen_rtx_MINUS (mode, XEXP (x, 1),
copy_rtx (XEXP (new, 0))), insn);
}
new = lookup_as_function (XEXP (x, 1), NEG);
if (new)
{
rtx new2 = lookup_as_function (XEXP (new, 0), NEG);
if (!new2)
return fold_rtx (gen_rtx_MINUS (mode, XEXP (x, 0),
copy_rtx (XEXP (new, 0))), insn);
}
if ( TARGET_FUSED_MADD && GET_MODE_CLASS (mode) == MODE_FLOAT )
{
new = lookup_as_function (XEXP (x, 0), MULT);
if (new)
{
rtx new2 = lookup_as_function (XEXP (new, 0), NEG);
if (new2)
{
rtx new3 = lookup_as_function (XEXP (new2, 0), NEG);
if (!new3)
return fold_rtx (gen_rtx_MINUS (mode, XEXP (x, 1),
gen_rtx (MULT, mode, copy_rtx (XEXP (new2, 0)),
copy_rtx (XEXP (new, 1)))), insn);
}
}
}
if ( TARGET_FUSED_MADD && GET_MODE_CLASS (mode) == MODE_FLOAT )
{
new = lookup_as_function (XEXP (x, 1), MULT);
if (new)
{
rtx new2 = lookup_as_function (XEXP (new, 0), NEG);
if (new2)
{
rtx new3 = lookup_as_function (XEXP (new2, 0), NEG);
if (!new3)
return fold_rtx (gen_rtx_MINUS (mode, XEXP (x, 0),
gen_rtx (MULT, mode, copy_rtx (XEXP (new2, 0)),
copy_rtx (XEXP (new, 1)))), insn);
}
}
}
break;
case MINUS:
new = lookup_as_function (XEXP (x, 1), NEG);
if (new)
{
rtx new2 = lookup_as_function (XEXP (new, 0), NEG);
rtx new3 = lookup_as_function (XEXP (x, 0), NEG);
if ( !new2 && !new3 )
return fold_rtx (gen_rtx_PLUS (mode, XEXP (x, 0),
copy_rtx (XEXP (new, 0))), insn);
}
if ( TARGET_FUSED_MADD && GET_MODE_CLASS (mode) == MODE_FLOAT )
{
new = lookup_as_function (XEXP (x, 1), MULT);
if (new)
{
rtx new2 = lookup_as_function (XEXP (new, 0), NEG);
if (new2)
{
rtx new3 = lookup_as_function (XEXP (new2, 0), NEG);
if (!new3)
{
return fold_rtx (gen_rtx_PLUS (mode,
gen_rtx (MULT, mode, copy_rtx (XEXP (new2, 0)),
copy_rtx (XEXP (new, 1))),
XEXP (x, 0)), insn);
}
}
}
}
break;
#endif
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 (GET_CODE (addr) == REG
&& 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;
}
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);
else if (GET_CODE (addr) == ADDRESSOF)
return change_address (x, VOIDmode, addr);
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);
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_if_possible (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 && GET_CODE (table_insn) == JUMP_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)))
return XVECEXP (table, 0,
offset / GET_MODE_SIZE (GET_MODE (table)));
}
if (table_insn && GET_CODE (table_insn) == JUMP_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:
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
&& GET_CODE (arg_ent->const_rtx) != REG
&& GET_CODE (arg_ent->const_rtx) != PLUS)
const_arg
= gen_lowpart_if_possible (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 (validate_change (insn, &XEXP (x, i), replacements[j], 0))
break;
if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c'
|| code == LTGT || code == UNEQ || code == ORDERED
|| code == UNORDERED)
{
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 (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c'
|| code == LTGT || code == UNEQ || code == ORDERED
|| code == UNORDERED)
{
if (must_swap || (const_arg0
&& (const_arg1 == 0
|| (GET_CODE (const_arg0) == CONST_INT
&& GET_CODE (const_arg1) != CONST_INT))))
{
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 '1':
{
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)
new = gen_rtx_CONST (mode, new);
}
break;
case '<':
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);
const_arg0 = equiv_constant (folded_arg0);
const_arg1 = equiv_constant (folded_arg1);
if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC)
break;
if (const_arg0 == 0 || const_arg1 == 0)
{
if (const_arg1 == const0_rtx
&& (NONZERO_BASE_PLUS_P (folded_arg0)
#if 0
|| GET_CODE (folded_arg0) == SYMBOL_REF
#endif
|| GET_CODE (folded_arg0) == LABEL_REF
|| GET_CODE (folded_arg0) == CONST))
{
if (code == EQ)
return false_rtx;
else if (code == NE)
return true_rtx;
}
if (folded_arg0 == folded_arg1
|| (GET_CODE (folded_arg0) == REG
&& GET_CODE (folded_arg1) == REG
&& (REG_QTY (REGNO (folded_arg0))
== REG_QTY (REGNO (folded_arg1))))
|| ((p0 = lookup (folded_arg0,
(safe_hash (folded_arg0, mode_arg0)
& HASH_MASK), mode_arg0))
&& (p1 = lookup (folded_arg1,
(safe_hash (folded_arg1, mode_arg0)
& HASH_MASK), 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 (GET_CODE (folded_arg0) == REG)
{
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))
|| (GET_CODE (folded_arg1) == REG
&& (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;
}
}
}
new = simplify_relational_operation (code,
(mode_arg0 != VOIDmode
? mode_arg0
: (GET_MODE (const_arg0
? const_arg0
: folded_arg0)
!= VOIDmode)
? GET_MODE (const_arg0
? const_arg0
: folded_arg0)
: GET_MODE (const_arg1
? const_arg1
: folded_arg1)),
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1);
#ifdef FLOAT_STORE_FLAG_VALUE
if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
{
if (new == const0_rtx)
new = CONST0_RTX (mode);
else
new = (CONST_DOUBLE_FROM_REAL_VALUE
(FLOAT_STORE_FLAG_VALUE (mode), mode));
}
#endif
break;
case '2':
case 'c':
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))
&& GET_CODE (folded_arg1) == REG)
{
rtx new_const = GEN_INT (-INTVAL (const_arg1));
struct table_elt *p
= lookup (new_const, safe_hash (new_const, mode) & HASH_MASK,
mode);
if (p)
for (p = p->first_same_value; p; p = p->next_same_value)
if (GET_CODE (p->exp) == REG)
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 (GET_CODE (folded_arg0) == REG
&& 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 && INTVAL (const_arg1) == INTVAL (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 'o':
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 '3':
case 'b':
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;
case 'x':
if (code == CONSTANT_P_RTX)
return (const_arg0 ? const1_rtx : const0_rtx);
break;
}
return new ? new : x;
}
static rtx
equiv_constant (x)
rtx x;
{
if (GET_CODE (x) == REG
&& 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_if_possible (GET_MODE (x), x_ent->const_rtx);
}
if (x == 0 || CONSTANT_P (x))
return x;
if (GET_CODE (x) == MEM)
{
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)) & HASH_MASK, 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 (mode, x)
enum machine_mode mode;
rtx x;
{
rtx result = gen_lowpart_common (mode, x);
if (result)
return result;
else if (GET_CODE (x) == MEM)
{
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 (insn, taken)
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 void
record_jump_cond (code, mode, op0, op1, reversed_nonequality)
enum rtx_code code;
enum machine_mode mode;
rtx op0, 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 = gen_lowpart_if_possible (inner_mode, op1);
record_jump_cond (code, mode, SUBREG_REG (op0),
tem ? tem : gen_rtx_SUBREG (inner_mode, op1, 0),
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 = gen_lowpart_if_possible (inner_mode, op0);
record_jump_cond (code, mode, SUBREG_REG (op1),
tem ? tem : gen_rtx_SUBREG (inner_mode, op0, 0),
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 = gen_lowpart_if_possible (inner_mode, op1);
record_jump_cond (code, mode, SUBREG_REG (op0),
tem ? tem : gen_rtx_SUBREG (inner_mode, op1, 0),
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 = gen_lowpart_if_possible (inner_mode, op0);
record_jump_cond (code, mode, SUBREG_REG (op1),
tem ? tem : gen_rtx_SUBREG (inner_mode, op0, 0),
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 (GET_CODE (op1) != REG)
op1 = equiv_constant (op1);
if ((reversed_nonequality && FLOAT_MODE_P (mode))
|| GET_CODE (op0) != REG || 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 (GET_CODE (op1) == REG)
{
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);
last_jump_equiv_class = op0_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 machine_mode mode;
rtx src_const;
rtx orig_src;
unsigned src_const_hash;
struct table_elt *src_const_elt;
};
static void
cse_insn (insn, libcall_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 (GET_CODE (insn) == CALL_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 = (struct set *) 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 = (struct set *) 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 (GET_CODE (clobbered) == REG
|| 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 (GET_CODE (XEXP (y, 0)) == MEM)
canon_reg (XEXP (y, 0), NULL_RTX);
}
else if (GET_CODE (y) == USE
&& ! (GET_CODE (XEXP (y, 0)) == REG
&& 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 (GET_CODE (XEXP (x, 0)) == MEM)
canon_reg (XEXP (x, 0), NULL_RTX);
}
else if (GET_CODE (x) == USE
&& ! (GET_CODE (XEXP (x, 0)) == REG
&& 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 ((GET_CODE (new) == REG && GET_CODE (src) == REG
&& ((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 || GET_CODE (dest) == SIGN_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) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SIGN_EXTRACT)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == MEM)
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;
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
|| GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_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 (GET_CODE (src) == MEM
&& find_reg_note (insn, REG_EQUIV, NULL_RTX) != 0
&& GET_CODE (dest) == REG
&& 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);
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)
{
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 (GET_CODE (const_elt->exp) == REG)
{
src_related = gen_lowpart_if_possible (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_if_possible (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 (GET_CODE (larger_elt->exp) == REG)
{
src_related
= gen_lowpart_if_possible (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
&& GET_CODE (src) == MEM && ! do_not_record
&& LOAD_EXTEND_OP (mode) != NIL)
{
enum machine_mode tmode;
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 (GET_CODE (larger_elt->exp) == REG)
{
src_related = gen_lowpart_if_possible (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, 0))
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 && GET_CODE (elt->exp) != REG
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
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
&& preferrable (src_folded_cost, src_folded_regcost,
src_cost, src_regcost) <= 0
&& preferrable (src_folded_cost, src_folded_regcost,
src_eqv_cost, src_eqv_regcost) <= 0
&& preferrable (src_folded_cost, src_folded_regcost,
src_related_cost, src_related_regcost) <= 0
&& preferrable (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)
trial = force_const_mem (mode, trial);
}
else if (src
&& preferrable (src_cost, src_regcost,
src_eqv_cost, src_eqv_regcost) <= 0
&& preferrable (src_cost, src_regcost,
src_related_cost, src_related_regcost) <= 0
&& preferrable (src_cost, src_regcost,
src_elt_cost, src_elt_regcost) <= 0)
trial = src, src_cost = MAX_COST;
else if (src_eqv_here
&& preferrable (src_eqv_cost, src_eqv_regcost,
src_related_cost, src_related_regcost) <= 0
&& preferrable (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
&& preferrable (src_related_cost, src_related_regcost,
src_elt_cost, src_elt_regcost) <= 0)
trial = copy_rtx (src_related), src_related_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))))
{
SET_SRC (sets[i].rtl) = trial;
cse_jumps_altered = 1;
break;
}
else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0))
{
if (libcall_insn
&& (GET_CODE (sets[i].orig_src) == REG
|| GET_CODE (sets[i].orig_src) == SUBREG
|| GET_CODE (sets[i].orig_src) == MEM))
replace_rtx (REG_NOTES (libcall_insn), sets[i].orig_src,
canon_reg (SET_SRC (sets[i].rtl), insn));
validate_change (insn, &SET_SRC (sets[i].rtl),
canon_reg (SET_SRC (sets[i].rtl), insn),
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
|| (GET_CODE (src_folded) != MEM
&& ! 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 = SET_SRC (sets[i].rtl);
if (GET_CODE (dest) == REG
&& 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)
&& GET_CODE (src) == REG && REGNO (src) == REGNO (dest)
&& (GET_CODE (sets[i].src) != REG
|| REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER)
&& (GET_CODE (dest) != REG || 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 && GET_CODE (dest) == REG
&& GET_CODE (src_const) != REG
&& ! (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))
{
src_const = copy_rtx (src_const);
set_unique_reg_note (insn, REG_EQUAL, src_const);
if (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->const_rtx == const0_rtx)
{
rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
rtx const_insn = dest_ent->const_insn;
if ((tem = single_set (const_insn)) != 0
&& rtx_equal_p (SET_DEST (tem), dest))
{
if (note)
XEXP (note, 0) = const_insn;
else
REG_NOTES (insn)
= gen_rtx_INSN_LIST (REG_WAS_0, const_insn,
REG_NOTES (insn));
}
}
}
}
do_not_record = 0;
while (GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
sets[i].inner_dest = dest;
if (GET_CODE (dest) == MEM)
{
#ifdef PUSH_ROUNDING
rtx addr = XEXP (dest, 0);
if (GET_RTX_CLASS (GET_CODE (addr)) == 'a'
&& 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
|| GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_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)
{
if (NEXT_INSN (insn) == 0
|| GET_CODE (NEXT_INSN (insn)) != BARRIER)
emit_barrier_after (insn);
if (n_sets == 1)
{
rtx new = emit_jump_insn_after (gen_jump (XEXP (src, 0)), insn);
JUMP_LABEL (new) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
delete_insn (insn);
insn = new;
if (NEXT_INSN (insn) == 0
|| GET_CODE (NEXT_INSN (insn)) != BARRIER)
emit_barrier_after (insn);
}
else
INSN_CODE (insn) = -1;
never_reached_warning (insn, NULL);
cse_jumps_altered = 1;
sets[i].rtl = 0;
}
else if (do_not_record)
{
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
invalidate (dest, VOIDmode);
else if (GET_CODE (dest) == MEM)
{
if (! libcall_insn || insn == libcall_insn)
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 (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 (GET_CODE (insn) == CALL_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 (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
invalidate (dest, VOIDmode);
else if (GET_CODE (dest) == MEM)
{
if (! libcall_insn || insn == libcall_insn)
invalidate (dest, VOIDmode);
}
else if (GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
}
if (GET_CODE (insn) == 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 (GET_CODE (x) != REG)
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);
rtx inner_dest = sets[i].inner_dest;
struct table_elt *elt;
if ((flag_float_store
&& GET_CODE (dest) == MEM
&& 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 (GET_CODE (dest) == REG || 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));
}
if (GET_CODE (inner_dest) == MEM
&& GET_CODE (XEXP (inner_dest, 0)) == ADDRESSOF)
elt = insert (dest, 0, sets[i].dest_hash, GET_MODE (dest));
else
elt = insert (dest, sets[i].src_elt,
sets[i].dest_hash, GET_MODE (dest));
elt->in_memory = (GET_CODE (sets[i].inner_dest) == MEM
&& (! RTX_UNCHANGING_P (sets[i].inner_dest)
|| FIXED_BASE_PLUS_P (XEXP (sets[i].inner_dest,
0))));
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 (GET_CODE (elt->exp) != REG
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
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
&& GET_CODE (classp->exp) != REG
&& ! exp_equiv_p (classp->exp, classp->exp, 1, 0))
classp = classp->next_same_value;
}
}
}
if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG
&& NEXT_INSN (PREV_INSN (insn)) == insn
&& GET_CODE (SET_SRC (sets[0].rtl)) == REG
&& 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 && GET_CODE (prev) == NOTE
&& NOTE_LINE_NUMBER (prev) != NOTE_INSN_BASIC_BLOCK);
if (prev != 0 && GET_CODE (prev) == INSN
&& 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 (prev, REG_WAS_0, NULL_RTX);
if (note)
remove_note (prev, note);
note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
if (note)
{
remove_note (insn, note);
XEXP (note, 1) = REG_NOTES (prev);
REG_NOTES (prev) = note;
}
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);
}
}
}
last_jump_equiv_class = 0;
if (GET_CODE (insn) == JUMP_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 && GET_CODE (prev_insn) == 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;
#endif
prev_insn = insn;
}
static void
invalidate_memory ()
{
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 (addr)
rtx addr;
{
if (GET_RTX_CLASS (GET_CODE (addr)) == 'a'
&& GET_CODE (XEXP (addr, 0)) == REG
&& 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 (x)
rtx x;
{
if (GET_CODE (x) == CLOBBER)
{
rtx ref = XEXP (x, 0);
if (ref)
{
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|| GET_CODE (ref) == MEM)
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 (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|| GET_CODE (ref) == MEM)
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 (x, object)
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)
|| GET_CODE (ent->const_rtx) == REG))
{
rtx new = gen_lowpart_if_possible (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
cse_around_loop (loop_start)
rtx loop_start;
{
rtx insn;
int i;
struct table_elt *p;
for (insn = PREV_INSN (loop_start);
insn && (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0);
insn = PREV_INSN (insn))
;
if (insn == 0
|| GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG)
return;
if (last_jump_equiv_class)
for (p = last_jump_equiv_class->first_same_value; p;
p = p->next_same_value)
{
if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG
|| (GET_CODE (p->exp) == SUBREG
&& GET_CODE (SUBREG_REG (p->exp)) == REG))
invalidate (p->exp, VOIDmode);
else if (GET_CODE (p->exp) == STRICT_LOW_PART
|| GET_CODE (p->exp) == ZERO_EXTRACT)
invalidate (XEXP (p->exp, 0), GET_MODE (p->exp));
}
for (insn = NEXT_INSN (loop_start);
GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != CODE_LABEL
&& INSN_UID (insn) < max_insn_uid
&& ! (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
insn = NEXT_INSN (insn))
{
if (INSN_P (insn)
&& (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER))
cse_set_around_loop (PATTERN (insn), insn, loop_start);
else if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER)
cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn,
loop_start);
}
}
static void
invalidate_skipped_set (dest, set, data)
rtx set;
rtx dest;
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
#ifdef HAVE_cc0
|| dest == cc0_rtx
#endif
|| 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 (start)
rtx start;
{
rtx insn;
for (insn = start; insn && GET_CODE (insn) != CODE_LABEL;
insn = NEXT_INSN (insn))
{
if (! INSN_P (insn))
continue;
if (GET_CODE (insn) == CALL_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_check_loop_start (x, set, data)
rtx x;
rtx set ATTRIBUTE_UNUSED;
void *data;
{
rtx *cse_check_loop_start_value = (rtx *) data;
if (*cse_check_loop_start_value == NULL_RTX
|| GET_CODE (x) == CC0 || GET_CODE (x) == PC)
return;
if ((GET_CODE (x) == MEM && GET_CODE (*cse_check_loop_start_value) == MEM)
|| reg_overlap_mentioned_p (x, *cse_check_loop_start_value))
*cse_check_loop_start_value = NULL_RTX;
}
static void
cse_set_around_loop (x, insn, loop_start)
rtx x;
rtx insn;
rtx loop_start;
{
struct table_elt *src_elt;
if (GET_CODE (x) == SET
&& GET_CODE (SET_DEST (x)) != PC && GET_CODE (SET_DEST (x)) != CC0
&& GET_CODE (SET_SRC (x)) != REG)
{
src_elt = lookup (SET_SRC (x),
HASH (SET_SRC (x), GET_MODE (SET_DEST (x))),
GET_MODE (SET_DEST (x)));
if (src_elt)
for (src_elt = src_elt->first_same_value; src_elt;
src_elt = src_elt->next_same_value)
if (GET_CODE (src_elt->exp) == REG && REG_LOOP_TEST_P (src_elt->exp)
&& COST (src_elt->exp) < COST (SET_SRC (x)))
{
rtx p, set;
for (p = prev_nonnote_insn (loop_start);
p && GET_CODE (p) != CALL_INSN
&& GET_CODE (p) != CODE_LABEL;
p = prev_nonnote_insn (p))
if ((set = single_set (p)) != 0
&& GET_CODE (SET_DEST (set)) == REG
&& GET_MODE (SET_DEST (set)) == src_elt->mode
&& rtx_equal_p (SET_SRC (set), SET_SRC (x)))
{
rtx q;
rtx cse_check_loop_start_value = SET_SRC (x);
for (q = p; q != loop_start; q = NEXT_INSN (q))
if (INSN_P (q))
note_stores (PATTERN (q),
cse_check_loop_start,
&cse_check_loop_start_value);
if (cse_check_loop_start_value
&& validate_change (insn, &SET_SRC (x),
src_elt->exp, 0))
{
int nregs = max_reg_num ();
rtx move
= gen_move_insn (src_elt->exp, SET_DEST (set));
if (nregs != max_reg_num ())
{
if (! validate_change (insn, &SET_SRC (x),
SET_SRC (set), 0))
abort ();
}
else
emit_insn_after (move, p);
}
break;
}
}
}
addr_affects_sp_p (SET_DEST (x));
if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
|| GET_CODE (SET_DEST (x)) == MEM)
invalidate (SET_DEST (x), VOIDmode);
else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
|| GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
invalidate (XEXP (SET_DEST (x), 0), GET_MODE (SET_DEST (x)));
}
void
cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks)
rtx insn;
struct cse_basic_block_data *data;
int follow_jumps;
int after_loop;
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 != NOT_TAKEN)
{
data->path[path_size - 1].status = NOT_TAKEN;
break;
}
else
path_size--;
}
if (GET_MODE (insn) == QImode)
follow_jumps = skip_blocks = 0;
while (p && GET_CODE (p) != CODE_LABEL)
{
if (! after_loop && GET_CODE (p) == NOTE
&& NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
break;
if (PREV_INSN (p) && GET_CODE (PREV_INSN (p)) == CALL_INSN
&& 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 (GET_CODE (p) != NOTE)
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 != NOT_TAKEN)
p = JUMP_LABEL (p);
path_entry++;
}
else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1
&& GET_CODE (p) == JUMP_INSN
&& 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 ((GET_CODE (q) != NOTE
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END
|| (PREV_INSN (q) && GET_CODE (PREV_INSN (q)) == CALL_INSN
&& find_reg_note (PREV_INSN (q), REG_SETJMP, NULL)))
&& (GET_CODE (q) != CODE_LABEL || LABEL_NUSES (q) != 0))
break;
if (follow_jumps && q != 0 && GET_CODE (q) == BARRIER)
{
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 = TAKEN;
path_size = path_entry;
p = JUMP_LABEL (p);
PUT_MODE (NEXT_INSN (p), QImode);
}
else if (skip_blocks && q != 0 && GET_CODE (q) != CODE_LABEL)
{
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 (GET_CODE (tmp) == CODE_LABEL)
break;
if (tmp == q)
{
data->path[path_entry].branch = p;
data->path[path_entry++].status = 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 != 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 (f, nregs, after_loop, file)
rtx f;
int nregs;
int after_loop;
FILE *file;
{
struct cse_basic_block_data val;
rtx insn = f;
int i;
cse_jumps_altered = 0;
recorded_label_ref = 0;
constant_pool_entries_cost = 0;
val.path_size = 0;
init_recog ();
init_alias_analysis ();
max_reg = nregs;
max_insn_uid = get_max_uid ();
reg_eqv_table = (struct reg_eqv_elem *)
xmalloc (nregs * sizeof (struct reg_eqv_elem));
#ifdef LOAD_EXTEND_OP
memory_extend_rtx = gen_rtx_ZERO_EXTEND (VOIDmode, NULL_RTX);
#endif
n_elements_made = 0;
max_uid = get_max_uid ();
uid_cuid = (int *) xcalloc (max_uid + 1, sizeof (int));
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) < 0)
INSN_CUID (insn) = ++i;
else
INSN_CUID (insn) = i;
}
ggc_push_context ();
insn = f;
while (insn)
{
cse_altered = 0;
cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps, after_loop,
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;
max_qty += max_reg;
if (val.path_size > 0)
cse_basic_block (insn, val.last, val.path, 0);
else
{
int old_cse_jumps_altered = cse_jumps_altered;
rtx temp;
cse_jumps_altered = 0;
temp = cse_basic_block (insn, val.last, val.path, ! after_loop);
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
}
ggc_pop_context ();
if (max_elements_made < n_elements_made)
max_elements_made = n_elements_made;
end_alias_analysis ();
free (uid_cuid);
free (reg_eqv_table);
return cse_jumps_altered || recorded_label_ref;
}
static rtx
cse_basic_block (from, to, next_branch, around_loop)
rtx from, to;
struct branch_path *next_branch;
int around_loop;
{
rtx insn;
int to_usage = 0;
rtx libcall_insn = NULL_RTX;
int num_insns = 0;
qty_table
= (struct qty_table_elem *) xmalloc ((max_qty - max_reg)
* sizeof (struct qty_table_elem));
qty_table -= max_reg;
new_basic_block ();
if (to != 0 && GET_CODE (to) == CODE_LABEL)
++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 != NOT_TAKEN)
{
if (status == TAKEN)
record_jump_equiv (insn, 1);
else
invalidate_skipped_block (NEXT_INSN (insn));
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
#endif
prev_insn = insn;
insn = JUMP_LABEL (insn);
continue;
}
}
if (GET_MODE (insn) == QImode)
PUT_MODE (insn, VOIDmode);
if (GET_RTX_CLASS (code) == 'i')
{
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))
libcall_insn = 0;
}
cse_insn (insn, libcall_insn);
if (GET_CODE (insn) == 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 + max_reg);
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
&& GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage)
{
struct cse_basic_block_data val;
rtx prev;
insn = NEXT_INSN (to);
if (insn == 0)
{
free (qty_table + max_reg);
return 0;
}
prev = prev_nonnote_insn (to);
if (prev && GET_CODE (prev) == BARRIER)
{
free (qty_table + max_reg);
return insn;
}
to_usage = 0;
val.path_size = 0;
cse_end_of_basic_block (insn, &val, 0, 0, 0);
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 && GET_CODE (to) == CODE_LABEL)
++LABEL_NUSES (to);
insn = PREV_INSN (insn);
}
}
if (next_qty > max_qty)
abort ();
insn = prev_nonnote_insn (to);
if ((cse_jumps_altered == 0
|| (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
&& around_loop && to != 0
&& GET_CODE (to) == NOTE && NOTE_LINE_NUMBER (to) == NOTE_INSN_LOOP_END
&& GET_CODE (insn) == JUMP_INSN
&& JUMP_LABEL (insn) != 0
&& LABEL_NUSES (JUMP_LABEL (insn)) == 1)
cse_around_loop (JUMP_LABEL (insn));
free (qty_table + max_reg);
return to ? NEXT_INSN (to) : 0;
}
static int
check_for_label_ref (rtl, data)
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 (x, counts, dest, incr)
rtx x;
int *counts;
rtx dest;
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:
if (x != dest)
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 (GET_CODE (XEXP (x, 0)) == MEM)
count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
return;
case SET:
if (GET_CODE (SET_DEST (x)) != REG)
count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
count_reg_usage (SET_SRC (x), counts,
SET_DEST (x),
incr);
return;
case CALL_INSN:
count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, NULL_RTX, incr);
case INSN:
case JUMP_INSN:
count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
note = find_reg_equal_equiv_note (x);
if (note)
count_reg_usage (XEXP (note, 0), counts, NULL_RTX, incr);
return;
case INSN_LIST:
abort ();
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, dest, incr);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
}
}
static bool
set_live_p (set, insn, counts)
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 (GET_CODE (SET_DEST (set)) != REG
|| REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
|| counts[REGNO (SET_DEST (set))] != 0
|| side_effects_p (SET_SRC (set))
|| (SET_DEST (set) == current_function_internal_arg_pointer))
return true;
return false;
}
static bool
insn_live_p (insn, counts)
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 (insn, counts)
rtx insn;
int *counts;
{
rtx note;
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (note)
{
rtx set = single_set (insn);
rtx new = simplify_rtx (XEXP (note, 0));
if (!new)
new = XEXP (note, 0);
count_reg_usage (insn, counts, NULL_RTX, -1);
if (set && validate_change (insn, &SET_SRC (set), new, 0))
{
count_reg_usage (insn, counts, NULL_RTX, 1);
remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
remove_note (insn, note);
return true;
}
count_reg_usage (insn, counts, NULL_RTX, 1);
}
return false;
}
int
delete_trivially_dead_insns (insns, nreg)
rtx insns;
int nreg;
{
int *counts;
rtx insn, prev;
int in_libcall = 0, dead_libcall = 0;
int ndead = 0, nlastdead, niterations = 0;
timevar_push (TV_DELETE_TRIVIALLY_DEAD);
counts = (int *) xcalloc (nreg, sizeof (int));
for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
count_reg_usage (insn, counts, NULL_RTX, 1);
do
{
nlastdead = ndead;
niterations++;
insn = get_last_insn ();
if (! INSN_P (insn))
insn = prev_real_insn (insn);
for (; insn; insn = prev)
{
int live_insn = 0;
prev = prev_real_insn (insn);
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, NULL_RTX, -1);
delete_insn_and_edges (insn);
ndead++;
}
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
{
in_libcall = 0;
dead_libcall = 0;
}
}
}
while (ndead != nlastdead);
if (rtl_dump_file && ndead)
fprintf (rtl_dump_file, "Deleted %i trivially dead insns; %i iterations\n",
ndead, niterations);
free (counts);
timevar_pop (TV_DELETE_TRIVIALLY_DEAD);
return ndead;
}