#include "config.h"
#include "system.h"
#include <setjmp.h>
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "expr.h"
#include "toplev.h"
#include "output.h"
#include "splay-tree.h"
static int max_reg;
static int max_insn_uid;
static int max_qty;
static int next_qty;
static int *qty_first_reg;
static int *qty_last_reg;
static enum machine_mode *qty_mode;
static rtx *qty_const;
static rtx *qty_const_insn;
static enum rtx_code *qty_comparison_code;
static rtx *qty_comparison_const;
static int *qty_comparison_qty;
#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;
static int *reg_next_eqv;
static int *reg_prev_eqv;
struct cse_reg_info {
union {
int reg_tick;
struct cse_reg_info* next;
} variant;
int reg_in_table;
int reg_qty;
};
static struct cse_reg_info *cse_reg_info_free_list;
static splay_tree cse_reg_info_tree;
static int cached_regno;
static struct cse_reg_info *cached_cse_reg_info;
static HARD_REG_SET hard_regs_in_table;
static HARD_REG_SET regs_invalidated_by_call;
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_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;
static int hash_arg_in_struct;
struct table_elt
{
rtx 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;
enum machine_mode mode;
char in_memory;
char in_struct;
char is_const;
char flag;
};
#define NBUCKETS 31
#define HASH(X, M) \
(GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) % NBUCKETS \
: canon_hash (X, M) % NBUCKETS)
#ifdef OVERLAPPING_REGNO_P
#define FIXED_REGNO_P(N) \
(((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| fixed_regs[N] || global_regs[N]) \
&& ! OVERLAPPING_REGNO_P ((N)))
#else
#define FIXED_REGNO_P(N) \
((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| fixed_regs[N] || global_regs[N])
#endif
#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 CHEAP_REG(N) \
((REG_USERVAR_P (N) && REGNO (N) < FIRST_PSEUDO_REGISTER) \
|| CHEAP_REGNO (REGNO (N)))
#define COST(X) \
(GET_CODE (X) == REG \
? (CHEAP_REG (X) ? 0 \
: REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1 \
: 2) \
: notreg_cost(X))
#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))->variant.reg_tick)
#define REG_IN_TABLE(N) ((GET_CSE_REG_INFO (N))->reg_in_table)
#define REG_QTY(N) ((GET_CSE_REG_INFO (N))->reg_qty)
#define REGNO_QTY_VALID_P(N) (REG_QTY (N) != (N))
#ifdef ADDRESS_COST
#define CSE_ADDRESS_COST(RTX) \
((GET_CODE (RTX) == ADDRESSOF && REG_P (XEXP ((RTX), 0))) \
? -1 : ADDRESS_COST(RTX))
#endif
static struct table_elt *table[NBUCKETS];
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 \
|| (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 \
|| XEXP (X, 0) == arg_pointer_rtx \
|| 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 \
|| XEXP (X, 0) == arg_pointer_rtx \
|| 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 PROTO((rtx));
static void new_basic_block PROTO((void));
static void make_new_qty PROTO((int));
static void make_regs_eqv PROTO((int, int));
static void delete_reg_equiv PROTO((int));
static int mention_regs PROTO((rtx));
static int insert_regs PROTO((rtx, struct table_elt *, int));
static void free_element PROTO((struct table_elt *));
static void remove_from_table PROTO((struct table_elt *, unsigned));
static struct table_elt *get_element PROTO((void));
static struct table_elt *lookup PROTO((rtx, unsigned, enum machine_mode)),
*lookup_for_remove PROTO((rtx, unsigned, enum machine_mode));
static rtx lookup_as_function PROTO((rtx, enum rtx_code));
static struct table_elt *insert PROTO((rtx, struct table_elt *, unsigned,
enum machine_mode));
static void merge_equiv_classes PROTO((struct table_elt *,
struct table_elt *));
static void invalidate PROTO((rtx, enum machine_mode));
static int cse_rtx_varies_p PROTO((rtx));
static void remove_invalid_refs PROTO((int));
static void remove_invalid_subreg_refs PROTO((int, int, enum machine_mode));
static void rehash_using_reg PROTO((rtx));
static void invalidate_memory PROTO((void));
static void invalidate_for_call PROTO((void));
static rtx use_related_value PROTO((rtx, struct table_elt *));
static unsigned canon_hash PROTO((rtx, enum machine_mode));
static unsigned safe_hash PROTO((rtx, enum machine_mode));
static int exp_equiv_p PROTO((rtx, rtx, int, int));
static void set_nonvarying_address_components PROTO((rtx, int, rtx *,
HOST_WIDE_INT *,
HOST_WIDE_INT *));
static int refers_to_p PROTO((rtx, rtx));
static rtx canon_reg PROTO((rtx, rtx));
static void find_best_addr PROTO((rtx, rtx *));
static enum rtx_code find_comparison_args PROTO((enum rtx_code, rtx *, rtx *,
enum machine_mode *,
enum machine_mode *));
static rtx cse_gen_binary PROTO((enum rtx_code, enum machine_mode,
rtx, rtx));
static rtx simplify_plus_minus PROTO((enum rtx_code, enum machine_mode,
rtx, rtx));
static rtx fold_rtx PROTO((rtx, rtx));
static rtx equiv_constant PROTO((rtx));
static void record_jump_equiv PROTO((rtx, int));
static void record_jump_cond PROTO((enum rtx_code, enum machine_mode,
rtx, rtx, int));
static void cse_insn PROTO((rtx, rtx));
static int note_mem_written PROTO((rtx));
static void invalidate_from_clobbers PROTO((rtx));
static rtx cse_process_notes PROTO((rtx, rtx));
static void cse_around_loop PROTO((rtx));
static void invalidate_skipped_set PROTO((rtx, rtx));
static void invalidate_skipped_block PROTO((rtx));
static void cse_check_loop_start PROTO((rtx, rtx));
static void cse_set_around_loop PROTO((rtx, rtx, rtx));
static rtx cse_basic_block PROTO((rtx, rtx, struct branch_path *, int));
static void count_reg_usage PROTO((rtx, int *, rtx, int));
extern void dump_class PROTO((struct table_elt*));
static void check_fold_consts PROTO((PTR));
static struct cse_reg_info* get_cse_reg_info PROTO((int));
static void free_cse_reg_info PROTO((splay_tree_value));
static void flush_hash_table PROTO((void));
extern int rtx_equal_function_value_matters;
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
notreg_cost (x)
rtx x;
{
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)))))
? (CHEAP_REG (SUBREG_REG (x)) ? 0
: (REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER ? 1
: 2))
: rtx_cost (x, SET) * 2);
}
#define COSTS_N_INSNS(N) ((N) * 4 - 2)
int
rtx_cost (x, outer_code)
rtx x;
enum rtx_code outer_code ATTRIBUTE_UNUSED;
{
register int i, j;
register enum rtx_code code;
register char *fmt;
register int total;
if (x == 0)
return 0;
code = GET_CODE (x);
switch (code)
{
case MULT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
total = 2;
else
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;
case ASM_OPERANDS:
total = 1000;
break;
default:
total = 2;
}
switch (code)
{
case REG:
return ! CHEAP_REG (x);
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);
return 2;
#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;
}
static struct cse_reg_info *
get_cse_reg_info (regno)
int regno;
{
struct cse_reg_info *cri;
splay_tree_node n;
n = splay_tree_lookup (cse_reg_info_tree,
(splay_tree_key) regno);
if (n)
cri = (struct cse_reg_info *) (n->value);
else
{
if (cse_reg_info_free_list)
{
cri = cse_reg_info_free_list;
cse_reg_info_free_list = cri->variant.next;
}
else
cri = (struct cse_reg_info *) xmalloc (sizeof (struct cse_reg_info));
cri->variant.reg_tick = 0;
cri->reg_in_table = -1;
cri->reg_qty = regno;
splay_tree_insert (cse_reg_info_tree,
(splay_tree_key) regno,
(splay_tree_value) cri);
}
cached_regno = regno;
cached_cse_reg_info = cri;
return cri;
}
static void
free_cse_reg_info (v)
splay_tree_value v;
{
struct cse_reg_info *cri = (struct cse_reg_info *) v;
cri->variant.next = cse_reg_info_free_list;
cse_reg_info_free_list = cri;
}
static void
new_basic_block ()
{
register int i;
next_qty = max_reg;
if (cse_reg_info_tree)
{
splay_tree_delete (cse_reg_info_tree);
cached_cse_reg_info = 0;
}
cse_reg_info_tree = splay_tree_new (splay_tree_compare_ints, 0,
free_cse_reg_info);
CLEAR_HARD_REG_SET (hard_regs_in_table);
for (i = 0; i < NBUCKETS; i++)
{
register struct table_elt *this, *next;
for (this = table[i]; this; this = next)
{
next = this->next_same_hash;
free_element (this);
}
}
bzero ((char *) table, sizeof table);
prev_insn = 0;
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
#endif
}
static void
make_new_qty (reg)
register int reg;
{
register int q;
if (next_qty >= max_qty)
abort ();
q = REG_QTY (reg) = next_qty++;
qty_first_reg[q] = reg;
qty_last_reg[q] = reg;
qty_const[q] = qty_const_insn[q] = 0;
qty_comparison_code[q] = UNKNOWN;
reg_next_eqv[reg] = reg_prev_eqv[reg] = -1;
}
static void
make_regs_eqv (new, old)
register int new, old;
{
register int lastr, firstr;
register int q = REG_QTY (old);
if (! REGNO_QTY_VALID_P (old))
abort ();
REG_QTY (new) = q;
firstr = qty_first_reg[q];
lastr = qty_last_reg[q];
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_prev_eqv[firstr] = new;
reg_next_eqv[new] = firstr;
reg_prev_eqv[new] = -1;
qty_first_reg[q] = new;
}
else
{
while (lastr < FIRST_PSEUDO_REGISTER && reg_prev_eqv[lastr] >= 0
&& (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr))
&& new >= FIRST_PSEUDO_REGISTER)
lastr = reg_prev_eqv[lastr];
reg_next_eqv[new] = reg_next_eqv[lastr];
if (reg_next_eqv[lastr] >= 0)
reg_prev_eqv[reg_next_eqv[lastr]] = new;
else
qty_last_reg[q] = new;
reg_next_eqv[lastr] = new;
reg_prev_eqv[new] = lastr;
}
}
static void
delete_reg_equiv (reg)
register int reg;
{
register int q = REG_QTY (reg);
register int p, n;
if (q == reg)
return;
p = reg_prev_eqv[reg];
n = reg_next_eqv[reg];
if (n != -1)
reg_prev_eqv[n] = p;
else
qty_last_reg[q] = p;
if (p != -1)
reg_next_eqv[p] = n;
else
qty_first_reg[q] = n;
REG_QTY (reg) = reg;
}
static int
mention_regs (x)
rtx x;
{
register enum rtx_code code;
register int i, j;
register char *fmt;
register int changed = 0;
if (x == 0)
return 0;
code = GET_CODE (x);
if (code == REG)
{
register int regno = REGNO (x);
register int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (x)));
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);
}
return 0;
}
if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
{
int i = REGNO (SUBREG_REG (x));
if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
{
if (REG_IN_TABLE (i) != REG_TICK (i) - 1)
remove_invalid_refs (i);
else
remove_invalid_subreg_refs (i, SUBREG_WORD (x), GET_MODE (x));
}
REG_IN_TABLE (i) = REG_TICK (i);
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_PTR, 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_PTR, 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)
{
register int regno = REGNO (x);
if (REGNO_QTY_VALID_P (regno)
&& qty_mode[REG_QTY (regno)] != GET_MODE (x))
return 0;
if (modified || ! REGNO_QTY_VALID_P (regno))
{
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;
}
make_new_qty (regno);
qty_mode[REG_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))))
{
int regno = REGNO (SUBREG_REG (x));
insert_regs (SUBREG_REG (x), NULL_PTR, 0);
if (REG_IN_TABLE (regno) >= 0
&& REG_TICK (regno) == REG_IN_TABLE (regno) + 1)
REG_TICK (regno)++;
mention_regs (x);
return 1;
}
else
return mention_regs (x);
}
static void
free_element (elt)
struct table_elt *elt;
{
elt->next_same_hash = free_element_chain;
free_element_chain = elt;
}
static struct table_elt *
get_element ()
{
struct table_elt *elt = free_element_chain;
if (elt)
{
free_element_chain = elt->next_same_hash;
return elt;
}
n_elements_made++;
return (struct table_elt *) oballoc (sizeof (struct table_elt));
}
static void
remove_from_table (elt, hash)
register struct table_elt *elt;
unsigned hash;
{
if (elt == 0)
return;
elt->first_same_value = 0;
{
register struct table_elt *prev = elt->prev_same_value;
register struct table_elt *next = elt->next_same_value;
if (next) next->prev_same_value = prev;
if (prev)
prev->next_same_value = next;
else
{
register struct table_elt *newfirst = next;
while (next)
{
next->first_same_value = newfirst;
next = next->next_same_value;
}
}
}
{
register struct table_elt *prev = elt->prev_same_hash;
register 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 < NBUCKETS; hash++)
if (table[hash] == elt)
table[hash] = next;
}
}
if (elt->related_value != 0 && elt->related_value != elt)
{
register 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;
}
free_element (elt);
}
static struct table_elt *
lookup (x, hash, mode)
rtx x;
unsigned hash;
enum machine_mode mode;
{
register 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;
{
register struct table_elt *p;
if (GET_CODE (x) == REG)
{
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;
{
register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS,
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) % NBUCKETS, 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) ((X)->cost < (Y)->cost)
static struct table_elt *
insert (x, classp, hash, mode)
register rtx x;
register struct table_elt *classp;
unsigned hash;
enum machine_mode mode;
{
register 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)
{
int regno = REGNO (x);
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
int i;
for (i = regno; i < endregno; i++)
SET_HARD_REG_BIT (hard_regs_in_table, i);
}
if (GET_CODE (x) == LABEL_REF
|| (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF))
recorded_label_ref = 1;
elt = get_element ();
elt->exp = x;
elt->cost = 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)
|| (RTX_UNCHANGING_P (x)
&& GET_CODE (x) == REG
&& 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))
{
register 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
{
register 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)
{
qty_const[REG_QTY (REGNO (classp->exp))]
= gen_lowpart_if_possible (qty_mode[REG_QTY (REGNO (classp->exp))], x);
qty_const_insn[REG_QTY (REGNO (classp->exp))] = this_insn;
}
else if (GET_CODE (x) == REG && classp && ! qty_const[REG_QTY (REGNO (x))]
&& ! elt->is_const)
{
register struct table_elt *p;
for (p = classp; p != 0; p = p->next_same_value)
{
if (p->is_const && GET_CODE (p->exp) != REG)
{
qty_const[REG_QTY (REGNO (x))]
= gen_lowpart_if_possible (GET_MODE (x), p->exp);
qty_const_insn[REG_QTY (REGNO (x))] = this_insn;
break;
}
}
}
else if (GET_CODE (x) == REG && qty_const[REG_QTY (REGNO (x))]
&& GET_MODE (x) == qty_mode[REG_QTY (REGNO (x))])
qty_const_insn[REG_QTY (REGNO (x))] = 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) % NBUCKETS;
subelt = lookup (subexp, subhash, mode);
if (subelt == 0)
subelt = insert (subexp, NULL_PTR, 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 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_arg_in_struct = 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;
new->in_struct = hash_arg_in_struct;
}
}
}
static void
flush_hash_table ()
{
int i;
struct table_elt *p;
for (i = 0; i < NBUCKETS; 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);
}
}
static void
invalidate (x, full_mode)
rtx x;
enum machine_mode full_mode;
{
register int i;
register struct table_elt *p;
if (GET_CODE (x) == REG)
{
register int regno = REGNO (x);
register unsigned hash = HASH (x, GET_MODE (x));
delete_reg_equiv (regno);
REG_TICK (regno)++;
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);
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
int tregno, tendregno;
register struct table_elt *p, *next;
CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
for (i = regno + 1; i < endregno; i++)
{
in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, i);
CLEAR_HARD_REG_BIT (hard_regs_in_table, i);
delete_reg_equiv (i);
REG_TICK (i)++;
}
if (in_table)
for (hash = 0; hash < NBUCKETS; 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;
}
if (GET_CODE (x) == SUBREG)
{
if (GET_CODE (SUBREG_REG (x)) != REG)
abort ();
invalidate (SUBREG_REG (x), VOIDmode);
return;
}
if (GET_CODE (x) == PARALLEL)
{
for (i = XVECLEN (x, 0) - 1; i >= 0 ; --i)
invalidate (XVECEXP (x, 0, i), VOIDmode);
return;
}
if (GET_CODE (x) == EXPR_LIST)
{
invalidate (XEXP (x, 0), VOIDmode);
return;
}
if (GET_CODE (x) != MEM)
abort ();
if (full_mode == VOIDmode)
full_mode = GET_MODE (x);
for (i = 0; i < NBUCKETS; i++)
{
register struct table_elt *next;
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory
&& (GET_CODE (p->exp) != MEM
|| true_dependence (x, full_mode, p->exp, cse_rtx_varies_p)))
remove_from_table (p, i);
}
}
}
static void
remove_invalid_refs (regno)
int regno;
{
register int i;
register struct table_elt *p, *next;
for (i = 0; i < NBUCKETS; 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, NULL_PTR))
remove_from_table (p, i);
}
}
static void
remove_invalid_subreg_refs (regno, word, mode)
int regno;
int word;
enum machine_mode mode;
{
register int i;
register struct table_elt *p, *next;
int end = word + (GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD;
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = next)
{
rtx exp;
next = p->next_same_hash;
exp = p->exp;
if (GET_CODE (p->exp) != REG
&& (GET_CODE (exp) != SUBREG
|| GET_CODE (SUBREG_REG (exp)) != REG
|| REGNO (SUBREG_REG (exp)) != regno
|| (((SUBREG_WORD (exp)
+ (GET_MODE_SIZE (GET_MODE (exp)) - 1) / UNITS_PER_WORD)
>= word)
&& SUBREG_WORD (exp) <= end))
&& refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR))
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 < NBUCKETS; 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) % NBUCKETS))
{
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 ()
{
int regno, endregno;
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)++;
in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
}
if (in_table)
for (hash = 0; hash < NBUCKETS; hash++)
for (p = table[hash]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory)
{
remove_from_table (p, hash);
continue;
}
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;
{
register struct table_elt *relt = 0;
register 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)) % NBUCKETS,
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 unsigned
canon_hash (x, mode)
rtx x;
enum machine_mode mode;
{
register int i, j;
register unsigned hash = 0;
register enum rtx_code code;
register char *fmt;
repeat:
if (x == 0)
return hash;
code = GET_CODE (x);
switch (code)
{
case REG:
{
register int regno = REGNO (x);
if (regno < FIRST_PSEUDO_REGISTER
&& (global_regs[regno]
|| (SMALL_REGISTER_CLASSES
&& ! fixed_regs[regno]
&& regno != FRAME_POINTER_REGNUM
&& regno != HARD_FRAME_POINTER_REGNUM
&& regno != ARG_POINTER_REGNUM
&& regno != STACK_POINTER_REGNUM
&& GET_MODE_CLASS (GET_MODE (x)) != MODE_CC)))
{
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_WORD (x));
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)
for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
{
unsigned tem = XINT (x, i);
hash += tem;
}
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
return hash;
case CONST_VECTOR:
hash += (unsigned) code + (unsigned) GET_MODE (x);
hash += (unsigned) CONST_VECTOR_0 (x);
hash += (unsigned) CONST_VECTOR_1 (x);
hash += (unsigned) CONST_VECTOR_2 (x);
hash += (unsigned) CONST_VECTOR_3 (x);
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))
{
do_not_record = 1;
return 0;
}
if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
{
hash_arg_in_memory = 1;
if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1;
}
hash += (unsigned) MEM;
x = XEXP (x, 0);
goto repeat;
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
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;
}
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')
{
register unsigned char *p = (unsigned char *) XSTR (x, i);
if (p)
while (*p)
hash += *p++;
}
else if (fmt[i] == 'i')
{
register unsigned tem = XINT (x, i);
hash += tem;
}
else if (fmt[i] == '0')
;
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;
int save_hash_arg_in_struct = hash_arg_in_struct;
unsigned hash = canon_hash (x, mode);
hash_arg_in_memory = save_hash_arg_in_memory;
hash_arg_in_struct = save_hash_arg_in_struct;
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;
{
register int i, j;
register enum rtx_code code;
register 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))
&& GET_MODE (y) == qty_mode[REG_QTY (REGNO (y))]
&& rtx_equal_p (x, qty_const[REG_QTY (REGNO (y))])
&& (! validate || REG_IN_TABLE (REGNO (y)) == REG_TICK (REGNO (y))))
return 1;
if (CONSTANT_P (y) && code == REG
&& REGNO_QTY_VALID_P (REGNO (x))
&& GET_MODE (x) == qty_mode[REG_QTY (REGNO (x))]
&& rtx_equal_p (y, qty_const[REG_QTY (REGNO (x))]))
return 1;
return 0;
}
if (GET_MODE (x) != GET_MODE (y))
return 0;
switch (code)
{
case PC:
case CC0:
return x == y;
case CONST_INT:
return INTVAL (x) == INTVAL (y);
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
case REG:
{
int regno = REGNO (y);
int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (y)));
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)));
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':
break;
default:
abort ();
}
}
return 1;
}
static int
refers_to_p (x, y)
rtx x, y;
{
register int i;
register enum rtx_code code;
register char *fmt;
repeat:
if (x == y)
return 1;
if (x == 0 || y == 0)
return 0;
code = GET_CODE (x);
if (code == GET_CODE (y))
{
if (exp_equiv_p (x, y, 0, 1))
return 1;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
{
if (i == 0)
{
x = XEXP (x, 0);
goto repeat;
}
else
if (refers_to_p (XEXP (x, i), y))
return 1;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
if (refers_to_p (XVECEXP (x, i, j), y))
return 1;
}
return 0;
}
static void
set_nonvarying_address_components (addr, size, pbase, pstart, pend)
rtx addr;
int size;
rtx *pbase;
HOST_WIDE_INT *pstart, *pend;
{
rtx base;
HOST_WIDE_INT start, end;
base = addr;
start = 0;
end = 0;
if (flag_pic && GET_CODE (base) == PLUS
&& XEXP (base, 0) == pic_offset_table_rtx)
base = XEXP (base, 1);
if (GET_CODE (base) == REG
&& qty_const != 0
&& REGNO_QTY_VALID_P (REGNO (base))
&& qty_mode[REG_QTY (REGNO (base))] == GET_MODE (base)
&& qty_const[REG_QTY (REGNO (base))] != 0)
base = qty_const[REG_QTY (REGNO (base))];
else if (GET_CODE (base) == PLUS
&& GET_CODE (XEXP (base, 1)) == CONST_INT
&& GET_CODE (XEXP (base, 0)) == REG
&& qty_const != 0
&& REGNO_QTY_VALID_P (REGNO (XEXP (base, 0)))
&& (qty_mode[REG_QTY (REGNO (XEXP (base, 0)))]
== GET_MODE (XEXP (base, 0)))
&& qty_const[REG_QTY (REGNO (XEXP (base, 0)))])
{
start = INTVAL (XEXP (base, 1));
base = qty_const[REG_QTY (REGNO (XEXP (base, 0)))];
}
else if (GET_CODE (base) == PLUS
&& GET_CODE (XEXP (base, 0)) == REG
&& GET_CODE (XEXP (base, 1)) == REG
&& qty_const != 0
&& REGNO_QTY_VALID_P (REGNO (XEXP (base, 0)))
&& (qty_mode[REG_QTY (REGNO (XEXP (base, 0)))]
== GET_MODE (XEXP (base, 0)))
&& qty_const[REG_QTY (REGNO (XEXP (base, 0)))]
&& REGNO_QTY_VALID_P (REGNO (XEXP (base, 1)))
&& (qty_mode[REG_QTY (REGNO (XEXP (base, 1)))]
== GET_MODE (XEXP (base, 1)))
&& qty_const[REG_QTY (REGNO (XEXP (base, 1)))])
{
rtx tem = qty_const[REG_QTY (REGNO (XEXP (base, 1)))];
base = qty_const[REG_QTY (REGNO (XEXP (base, 0)))];
if (GET_CODE (base) != CONST_INT)
{
if (GET_CODE (tem) != CONST_INT)
abort ();
start = INTVAL (tem);
}
else
{
start = INTVAL (base);
base = tem;
}
}
while (1)
{
switch (GET_CODE (base))
{
case LO_SUM:
base = XEXP (base, 1);
continue;
case CONST:
base = XEXP (base, 0);
continue;
case PLUS:
if (GET_CODE (XEXP (base, 1)) == CONST_INT)
{
start += INTVAL (XEXP (base, 1));
base = XEXP (base, 0);
continue;
}
break;
case AND:
if (GET_CODE (XEXP (base, 1)) == CONST_INT
&& exact_log2 (- INTVAL (XEXP (base, 1))) > 0)
{
set_nonvarying_address_components (XEXP (base, 0), size,
pbase, pstart, pend);
size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
start += *pstart + INTVAL (XEXP (base, 1)) + 1;
end += *pend;
base = *pbase;
}
break;
default:
break;
}
break;
}
if (GET_CODE (base) == CONST_INT)
{
start += INTVAL (base);
base = const0_rtx;
}
end = start + size;
*pbase = base;
*pstart = start;
*pend = end;
}
static int
cse_rtx_varies_p (x)
register rtx x;
{
if (GET_CODE (x) == REG
&& REGNO_QTY_VALID_P (REGNO (x))
&& GET_MODE (x) == qty_mode[REG_QTY (REGNO (x))]
&& qty_const[REG_QTY (REGNO (x))] != 0)
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)))
&& (GET_MODE (XEXP (x, 0))
== qty_mode[REG_QTY (REGNO (XEXP (x, 0)))])
&& qty_const[REG_QTY (REGNO (XEXP (x, 0)))])
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)))
&& (GET_MODE (XEXP (x, 0))
== qty_mode[REG_QTY (REGNO (XEXP (x, 0)))])
&& qty_const[REG_QTY (REGNO (XEXP (x, 0)))]
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))
&& (GET_MODE (XEXP (x, 1))
== qty_mode[REG_QTY (REGNO (XEXP (x, 1)))])
&& qty_const[REG_QTY (REGNO (XEXP (x, 1)))])
return 0;
return rtx_varies_p (x);
}
static rtx
canon_reg (x, insn)
rtx x;
rtx insn;
{
register int i;
register enum rtx_code code;
register 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 SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return x;
case REG:
{
register int first;
if (REGNO (x) < FIRST_PSEUDO_REGISTER
|| ! REGNO_QTY_VALID_P (REGNO (x)))
return x;
first = qty_first_reg[REG_QTY (REGNO (x))];
return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first]
: REGNO_REG_CLASS (first) == NO_REGS ? x
: gen_rtx_REG (qty_mode[REG_QTY (REGNO (x))], first));
}
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
register 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_n_dups[insn_code] > 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)
rtx insn;
rtx *loc;
{
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 save_hash_arg_in_struct = hash_arg_in_struct;
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);
if (1
#ifdef ADDRESS_COST
&& (CSE_ADDRESS_COST (folded) < CSE_ADDRESS_COST (addr)
|| (CSE_ADDRESS_COST (folded) == CSE_ADDRESS_COST (addr)
&& rtx_cost (folded, MEM) > rtx_cost (addr, MEM)))
#else
&& rtx_cost (folded, MEM) < rtx_cost (addr, MEM)
#endif
&& 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;
hash_arg_in_struct = save_hash_arg_in_struct;
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 = CSE_ADDRESS_COST (*loc);
int best_rtx_cost = (elt->cost + 1) >> 1;
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))
&& (CSE_ADDRESS_COST (p->exp) < best_addr_cost
|| (CSE_ADDRESS_COST (p->exp) == best_addr_cost
&& (p->cost + 1) >> 1 > best_rtx_cost)))
{
found_better = 1;
best_addr_cost = CSE_ADDRESS_COST (p->exp);
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;
hash_arg_in_struct = save_hash_arg_in_struct;
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 = CSE_ADDRESS_COST (*loc);
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 = cse_gen_binary (GET_CODE (*loc), Pmode, p->exp, c);
if ((CSE_ADDRESS_COST (new) < best_addr_cost
|| (CSE_ADDRESS_COST (new) == best_addr_cost
&& (COST (new) + 1) >> 1 > best_rtx_cost)))
{
found_better = 1;
best_addr_cost = CSE_ADDRESS_COST (new);
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)) == '<')
{
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
&& FLOAT_STORE_FLAG_VALUE < 0)
#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
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
)
x = arg1, reverse_code = 1;
}
if (x == 0)
p = lookup (arg1, safe_hash (arg1, GET_MODE (arg1)) % NBUCKETS,
GET_MODE (arg1));
if (p) p = p->first_same_value;
for (; p; p = p->next_same_value)
{
enum machine_mode inner_mode = GET_MODE (p->exp);
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
&& FLOAT_STORE_FLAG_VALUE < 0)
#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
&& FLOAT_STORE_FLAG_VALUE < 0)
#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;
arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
if (GET_RTX_CLASS (GET_CODE (x)) == '<')
code = GET_CODE (x);
if (reverse_code)
code = reverse_condition (code);
}
*pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
*parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
return code;
}
rtx
simplify_unary_operation (code, mode, op, op_mode)
enum rtx_code code;
enum machine_mode mode;
rtx op;
enum machine_mode op_mode;
{
register int width = GET_MODE_BITSIZE (mode);
#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
if (code == FLOAT && GET_MODE (op) == VOIDmode
&& (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
{
HOST_WIDE_INT hv, lv;
REAL_VALUE_TYPE d;
if (GET_CODE (op) == CONST_INT)
lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
else
lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op);
#ifdef REAL_ARITHMETIC
REAL_VALUE_FROM_INT (d, lv, hv, mode);
#else
if (hv < 0)
{
d = (double) (~ hv);
d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
* (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
d += (double) (unsigned HOST_WIDE_INT) (~ lv);
d = (- d - 1.0);
}
else
{
d = (double) hv;
d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
* (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
d += (double) (unsigned HOST_WIDE_INT) lv;
}
#endif
d = real_value_truncate (mode, d);
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode
&& (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
{
HOST_WIDE_INT hv, lv;
REAL_VALUE_TYPE d;
if (GET_CODE (op) == CONST_INT)
lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
else
lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op);
if (op_mode == VOIDmode)
{
if (hv < 0)
return 0;
}
else if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
;
else
hv = 0, lv &= GET_MODE_MASK (op_mode);
#ifdef REAL_ARITHMETIC
REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
#else
d = (double) (unsigned HOST_WIDE_INT) hv;
d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
* (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
d += (double) (unsigned HOST_WIDE_INT) lv;
#endif
d = real_value_truncate (mode, d);
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
#endif
if (GET_CODE (op) == CONST_INT
&& width <= HOST_BITS_PER_WIDE_INT && width > 0)
{
register HOST_WIDE_INT arg0 = INTVAL (op);
register HOST_WIDE_INT val;
switch (code)
{
case NOT:
val = ~ arg0;
break;
case NEG:
val = - arg0;
break;
case ABS:
val = (arg0 >= 0 ? arg0 : - arg0);
break;
case FFS:
arg0 &= GET_MODE_MASK (mode);
val = exact_log2 (arg0 & (- arg0)) + 1;
break;
case TRUNCATE:
val = arg0;
break;
case ZERO_EXTEND:
if (op_mode == VOIDmode)
op_mode = mode;
if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
{
if (width != GET_MODE_BITSIZE (op_mode))
abort ();
val = arg0;
}
else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
else
return 0;
break;
case SIGN_EXTEND:
if (op_mode == VOIDmode)
op_mode = mode;
if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
{
if (width != GET_MODE_BITSIZE (op_mode))
abort ();
val = arg0;
}
else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
{
val
= arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
if (val
& ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1)))
val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
}
else
return 0;
break;
case SQRT:
return 0;
default:
abort ();
}
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= ((HOST_WIDE_INT) (-1) << width);
return GEN_INT (val);
}
else if (GET_MODE (op) == VOIDmode && width <= HOST_BITS_PER_INT * 2
&& (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
{
HOST_WIDE_INT l1, h1, lv, hv;
if (GET_CODE (op) == CONST_DOUBLE)
l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op);
else
l1 = INTVAL (op), h1 = l1 < 0 ? -1 : 0;
switch (code)
{
case NOT:
lv = ~ l1;
hv = ~ h1;
break;
case NEG:
neg_double (l1, h1, &lv, &hv);
break;
case ABS:
if (h1 < 0)
neg_double (l1, h1, &lv, &hv);
else
lv = l1, hv = h1;
break;
case FFS:
hv = 0;
if (l1 == 0)
lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & (-h1)) + 1;
else
lv = exact_log2 (l1 & (-l1)) + 1;
break;
case TRUNCATE:
lv = l1, hv = h1;
break;
case ZERO_EXTEND:
if (op_mode == VOIDmode
|| GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
return 0;
hv = 0;
lv = l1 & GET_MODE_MASK (op_mode);
break;
case SIGN_EXTEND:
if (op_mode == VOIDmode
|| GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
return 0;
else
{
lv = l1 & GET_MODE_MASK (op_mode);
if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT
&& (lv & ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (op_mode) - 1))) != 0)
lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
hv = (lv < 0) ? ~ (HOST_WIDE_INT) 0 : 0;
}
break;
case SQRT:
return 0;
default:
return 0;
}
return immed_double_const (lv, hv, mode);
}
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE_CLASS (mode) == MODE_FLOAT)
{
REAL_VALUE_TYPE d;
jmp_buf handler;
rtx x;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
switch (code)
{
case NEG:
d = REAL_VALUE_NEGATE (d);
break;
case ABS:
if (REAL_VALUE_NEGATIVE (d))
d = REAL_VALUE_NEGATE (d);
break;
case FLOAT_TRUNCATE:
d = real_value_truncate (mode, d);
break;
case FLOAT_EXTEND:
break;
case FIX:
d = REAL_VALUE_RNDZINT (d);
break;
case UNSIGNED_FIX:
d = REAL_VALUE_UNSIGNED_RNDZINT (d);
break;
case SQRT:
return 0;
default:
abort ();
}
x = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
set_float_handler (NULL_PTR);
return x;
}
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_MODE_CLASS (mode) == MODE_INT
&& width <= HOST_BITS_PER_WIDE_INT && width > 0)
{
REAL_VALUE_TYPE d;
jmp_buf handler;
HOST_WIDE_INT val;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
switch (code)
{
case FIX:
val = REAL_VALUE_FIX (d);
break;
case UNSIGNED_FIX:
val = REAL_VALUE_UNSIGNED_FIX (d);
break;
default:
abort ();
}
set_float_handler (NULL_PTR);
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= ((HOST_WIDE_INT) (-1) << width);
return GEN_INT (val);
}
#endif
else
{
switch (code)
{
case NEG:
case NOT:
if (GET_CODE (op) == code)
return XEXP (op, 0);
break;
case SIGN_EXTEND:
if (GET_CODE (op) == TRUNCATE
&& GET_MODE (XEXP (op, 0)) == mode
&& GET_CODE (XEXP (op, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF
&& GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
return XEXP (op, 0);
#ifdef POINTERS_EXTEND_UNSIGNED
if (! POINTERS_EXTEND_UNSIGNED
&& mode == Pmode && GET_MODE (op) == ptr_mode
&& CONSTANT_P (op))
return convert_memory_address (Pmode, op);
#endif
break;
#ifdef POINTERS_EXTEND_UNSIGNED
case ZERO_EXTEND:
if (POINTERS_EXTEND_UNSIGNED
&& mode == Pmode && GET_MODE (op) == ptr_mode
&& CONSTANT_P (op))
return convert_memory_address (Pmode, op);
break;
#endif
default:
break;
}
return 0;
}
}
rtx
simplify_binary_operation (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
register HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
HOST_WIDE_INT val;
int width = GET_MODE_BITSIZE (mode);
rtx tem;
if (GET_RTX_CLASS (code) == '<')
abort ();
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
&& mode == GET_MODE (op0) && mode == GET_MODE (op1))
{
REAL_VALUE_TYPE f0, f1, value;
jmp_buf handler;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (f0, op0);
REAL_VALUE_FROM_CONST_DOUBLE (f1, op1);
f0 = real_value_truncate (mode, f0);
f1 = real_value_truncate (mode, f1);
#ifdef REAL_ARITHMETIC
#ifndef REAL_INFINITY
if (code == DIV && REAL_VALUES_EQUAL (f1, dconst0))
return 0;
#endif
REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
#else
switch (code)
{
case PLUS:
value = f0 + f1;
break;
case MINUS:
value = f0 - f1;
break;
case MULT:
value = f0 * f1;
break;
case DIV:
#ifndef REAL_INFINITY
if (f1 == 0)
return 0;
#endif
value = f0 / f1;
break;
case SMIN:
value = MIN (f0, f1);
break;
case SMAX:
value = MAX (f0, f1);
break;
default:
abort ();
}
#endif
value = real_value_truncate (mode, value);
set_float_handler (NULL_PTR);
return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
}
#endif
if (GET_MODE_CLASS (mode) == MODE_INT
&& width == HOST_BITS_PER_WIDE_INT * 2
&& (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
&& (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
{
HOST_WIDE_INT l1, l2, h1, h2, lv, hv;
if (GET_CODE (op0) == CONST_DOUBLE)
l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
else
l1 = INTVAL (op0), h1 = l1 < 0 ? -1 : 0;
if (GET_CODE (op1) == CONST_DOUBLE)
l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1);
else
l2 = INTVAL (op1), h2 = l2 < 0 ? -1 : 0;
switch (code)
{
case MINUS:
neg_double (l2, h2, &lv, &hv);
l2 = lv, h2 = hv;
case PLUS:
add_double (l1, h1, l2, h2, &lv, &hv);
break;
case MULT:
mul_double (l1, h1, l2, h2, &lv, &hv);
break;
case DIV: case MOD: case UDIV: case UMOD:
return 0;
case AND:
lv = l1 & l2, hv = h1 & h2;
break;
case IOR:
lv = l1 | l2, hv = h1 | h2;
break;
case XOR:
lv = l1 ^ l2, hv = h1 ^ h2;
break;
case SMIN:
if (h1 < h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
< (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case SMAX:
if (h1 > h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
> (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case UMIN:
if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
< (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case UMAX:
if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
> (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case LSHIFTRT: case ASHIFTRT:
case ASHIFT:
case ROTATE: case ROTATERT:
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0;
#endif
if (h2 != 0 || l2 < 0 || l2 >= GET_MODE_BITSIZE (mode))
return 0;
if (code == LSHIFTRT || code == ASHIFTRT)
rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
code == ASHIFTRT);
else if (code == ASHIFT)
lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, 1);
else if (code == ROTATE)
lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
else
rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
break;
default:
return 0;
}
return immed_double_const (lv, hv, mode);
}
if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT
|| width > HOST_BITS_PER_WIDE_INT || width == 0)
{
switch (code)
{
case PLUS:
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& FLOAT_MODE_P (mode) && ! flag_fast_math)
break;
if (op1 == CONST0_RTX (mode))
return op0;
if (GET_CODE (op0) == NEG)
return cse_gen_binary (MINUS, mode, op1, XEXP (op0, 0));
else if (GET_CODE (op1) == NEG)
return cse_gen_binary (MINUS, mode, op0, XEXP (op1, 0));
if (CONSTANT_P (op0) && GET_MODE (op0) != VOIDmode
&& GET_CODE (op1) == CONST_INT)
return plus_constant (op0, INTVAL (op1));
else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode
&& GET_CODE (op0) == CONST_INT)
return plus_constant (op1, INTVAL (op0));
if (! FLOAT_MODE_P (mode))
{
HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
rtx lhs = op0, rhs = op1;
int had_mult = 0;
if (GET_CODE (lhs) == NEG)
coeff0 = -1, lhs = XEXP (lhs, 0);
else if (GET_CODE (lhs) == MULT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT)
{
coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
had_mult = 1;
}
else if (GET_CODE (lhs) == ASHIFT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT
&& INTVAL (XEXP (lhs, 1)) >= 0
&& INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
lhs = XEXP (lhs, 0);
}
if (GET_CODE (rhs) == NEG)
coeff1 = -1, rhs = XEXP (rhs, 0);
else if (GET_CODE (rhs) == MULT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT)
{
coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
had_mult = 1;
}
else if (GET_CODE (rhs) == ASHIFT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT
&& INTVAL (XEXP (rhs, 1)) >= 0
&& INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
rhs = XEXP (rhs, 0);
}
if (rtx_equal_p (lhs, rhs))
{
tem = cse_gen_binary (MULT, mode, lhs,
GEN_INT (coeff0 + coeff1));
return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
}
}
if (INTEGRAL_MODE_P (mode)
&& (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|| GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
&& (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
return tem;
break;
case COMPARE:
#ifdef HAVE_cc0
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op1 == CONST0_RTX (mode))
return op0;
#else
#endif
break;
case MINUS:
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& FLOAT_MODE_P (mode) && ! flag_fast_math)
break;
if (rtx_equal_p (op0, op1)
&& ! side_effects_p (op0)
&& (! FLOAT_MODE_P (mode) || flag_fast_math))
return CONST0_RTX (mode);
if (op0 == CONST0_RTX (mode))
return gen_rtx_NEG (mode, op1);
if (op0 == constm1_rtx)
return gen_rtx_NOT (mode, op1);
if (op1 == CONST0_RTX (mode))
return op0;
if (! FLOAT_MODE_P (mode))
{
HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
rtx lhs = op0, rhs = op1;
int had_mult = 0;
if (GET_CODE (lhs) == NEG)
coeff0 = -1, lhs = XEXP (lhs, 0);
else if (GET_CODE (lhs) == MULT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT)
{
coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
had_mult = 1;
}
else if (GET_CODE (lhs) == ASHIFT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT
&& INTVAL (XEXP (lhs, 1)) >= 0
&& INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
lhs = XEXP (lhs, 0);
}
if (GET_CODE (rhs) == NEG)
coeff1 = - 1, rhs = XEXP (rhs, 0);
else if (GET_CODE (rhs) == MULT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT)
{
coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
had_mult = 1;
}
else if (GET_CODE (rhs) == ASHIFT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT
&& INTVAL (XEXP (rhs, 1)) >= 0
&& INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
rhs = XEXP (rhs, 0);
}
if (rtx_equal_p (lhs, rhs))
{
tem = cse_gen_binary (MULT, mode, lhs,
GEN_INT (coeff0 - coeff1));
return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
}
}
if (GET_CODE (op1) == NEG)
return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
if (INTEGRAL_MODE_P (mode)
&& (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|| GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
&& (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
return tem;
if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
return plus_constant (op0, - INTVAL (op1));
if (GET_CODE (op1) == AND)
{
if (rtx_equal_p (op0, XEXP (op1, 0)))
return cse_gen_binary (AND, mode, op0, gen_rtx_NOT (mode, XEXP (op1, 1)));
if (rtx_equal_p (op0, XEXP (op1, 1)))
return cse_gen_binary (AND, mode, op0, gen_rtx_NOT (mode, XEXP (op1, 0)));
}
break;
case MULT:
if (op1 == constm1_rtx)
{
tem = simplify_unary_operation (NEG, mode, op0, mode);
return tem ? tem : gen_rtx_NEG (mode, op0);
}
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op1 == CONST0_RTX (mode)
&& ! side_effects_p (op0))
return op1;
if (op1 == CONST1_RTX (mode))
return op0;
if (GET_CODE (op1) == CONST_INT
&& (val = exact_log2 (INTVAL (op1))) >= 0
&& (width <= HOST_BITS_PER_WIDE_INT
|| val != HOST_BITS_PER_WIDE_INT - 1)
&& ! rtx_equal_function_value_matters)
return gen_rtx_ASHIFT (mode, op0, GEN_INT (val));
if (GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT)
{
REAL_VALUE_TYPE d;
jmp_buf handler;
int op1is2, op1ism1;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
op1is2 = REAL_VALUES_EQUAL (d, dconst2);
op1ism1 = REAL_VALUES_EQUAL (d, dconstm1);
set_float_handler (NULL_PTR);
if (op1is2 && GET_MODE (op0) == mode)
return gen_rtx_PLUS (mode, op0, copy_rtx (op0));
else if (op1ism1 && GET_MODE (op0) == mode)
return gen_rtx_NEG (mode, op0);
}
break;
case IOR:
if (op1 == const0_rtx)
return op0;
if (GET_CODE (op1) == CONST_INT
&& (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
return op1;
if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
|| (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0)))
&& ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return constm1_rtx;
break;
case XOR:
if (op1 == const0_rtx)
return op0;
if (GET_CODE (op1) == CONST_INT
&& (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
return gen_rtx_NOT (mode, op0);
if (op0 == op1 && ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return const0_rtx;
break;
case AND:
if (op1 == const0_rtx && ! side_effects_p (op0))
return const0_rtx;
if (GET_CODE (op1) == CONST_INT
&& (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
return op0;
if (op0 == op1 && ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return op0;
if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
|| (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0)))
&& ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return const0_rtx;
break;
case UDIV:
if (GET_CODE (op1) == CONST_INT
&& (arg1 = exact_log2 (INTVAL (op1))) > 0)
return gen_rtx_LSHIFTRT (mode, op0, GEN_INT (arg1));
case DIV:
if (op1 == CONST1_RTX (mode))
return op0;
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op0 == CONST0_RTX (mode)
&& ! side_effects_p (op1))
return op0;
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
else if (GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT
&& op1 != CONST0_RTX (mode)
&& flag_fast_math)
{
REAL_VALUE_TYPE d;
REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
if (! REAL_VALUES_EQUAL (d, dconst0))
{
#if defined (REAL_ARITHMETIC)
REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
return gen_rtx_MULT (mode, op0,
CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
#else
return gen_rtx_MULT (mode, op0,
CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
#endif
}
}
#endif
break;
case UMOD:
if (GET_CODE (op1) == CONST_INT
&& exact_log2 (INTVAL (op1)) > 0)
return gen_rtx_AND (mode, op0, GEN_INT (INTVAL (op1) - 1));
case MOD:
if ((op0 == const0_rtx || op1 == const1_rtx)
&& ! side_effects_p (op0) && ! side_effects_p (op1))
return const0_rtx;
break;
case ROTATERT:
case ROTATE:
if (GET_CODE (op0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT
&& INTVAL (op0) == GET_MODE_MASK (mode)
&& ! side_effects_p (op1))
return op0;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (op1 == const0_rtx)
return op0;
if (op0 == const0_rtx && ! side_effects_p (op1))
return op0;
break;
case SMIN:
if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == (HOST_WIDE_INT) 1 << (width -1)
&& ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
case SMAX:
if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
&& (INTVAL (op1)
== (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1)
&& ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
case UMIN:
if (op1 == const0_rtx && ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
case UMAX:
if (op1 == constm1_rtx && ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
default:
abort ();
}
return 0;
}
arg0 = INTVAL (op0);
arg1 = INTVAL (op1);
if (width < HOST_BITS_PER_WIDE_INT)
{
arg0 &= ((HOST_WIDE_INT) 1 << width) - 1;
arg1 &= ((HOST_WIDE_INT) 1 << width) - 1;
arg0s = arg0;
if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1)))
arg0s |= ((HOST_WIDE_INT) (-1) << width);
arg1s = arg1;
if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1)))
arg1s |= ((HOST_WIDE_INT) (-1) << width);
}
else
{
arg0s = arg0;
arg1s = arg1;
}
switch (code)
{
case PLUS:
val = arg0s + arg1s;
break;
case MINUS:
val = arg0s - arg1s;
break;
case MULT:
val = arg0s * arg1s;
break;
case DIV:
if (arg1s == 0)
return 0;
val = arg0s / arg1s;
break;
case MOD:
if (arg1s == 0)
return 0;
val = arg0s % arg1s;
break;
case UDIV:
if (arg1 == 0)
return 0;
val = (unsigned HOST_WIDE_INT) arg0 / arg1;
break;
case UMOD:
if (arg1 == 0)
return 0;
val = (unsigned HOST_WIDE_INT) arg0 % arg1;
break;
case AND:
val = arg0 & arg1;
break;
case IOR:
val = arg0 | arg1;
break;
case XOR:
val = arg0 ^ arg1;
break;
case LSHIFTRT:
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
#endif
val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
break;
case ASHIFT:
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
#endif
val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
break;
case ASHIFTRT:
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
#endif
val = arg0s >> arg1;
if (arg0s < 0 && arg1 > 0)
val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1);
break;
case ROTATERT:
if (arg1 < 0)
return 0;
arg1 %= width;
val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1))
| (((unsigned HOST_WIDE_INT) arg0) >> arg1));
break;
case ROTATE:
if (arg1 < 0)
return 0;
arg1 %= width;
val = ((((unsigned HOST_WIDE_INT) arg0) << arg1)
| (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1)));
break;
case COMPARE:
return 0;
case SMIN:
val = arg0s <= arg1s ? arg0s : arg1s;
break;
case UMIN:
val = ((unsigned HOST_WIDE_INT) arg0
<= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
break;
case SMAX:
val = arg0s > arg1s ? arg0s : arg1s;
break;
case UMAX:
val = ((unsigned HOST_WIDE_INT) arg0
> (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
break;
default:
abort ();
}
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= ((HOST_WIDE_INT) (-1) << width);
return GEN_INT (val);
}
static rtx
simplify_plus_minus (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
rtx ops[8];
int negs[8];
rtx result, tem;
int n_ops = 2, input_ops = 2, input_consts = 0, n_consts = 0;
int first = 1, negate = 0, changed;
int i, j;
bzero ((char *) ops, sizeof ops);
ops[0] = op0, ops[1] = op1, negs[0] = 0, negs[1] = (code == MINUS);
changed = 1;
while (changed)
{
changed = 0;
for (i = 0; i < n_ops; i++)
switch (GET_CODE (ops[i]))
{
case PLUS:
case MINUS:
if (n_ops == 7)
return 0;
ops[n_ops] = XEXP (ops[i], 1);
negs[n_ops++] = GET_CODE (ops[i]) == MINUS ? !negs[i] : negs[i];
ops[i] = XEXP (ops[i], 0);
input_ops++;
changed = 1;
break;
case NEG:
ops[i] = XEXP (ops[i], 0);
negs[i] = ! negs[i];
changed = 1;
break;
case CONST:
ops[i] = XEXP (ops[i], 0);
input_consts++;
changed = 1;
break;
case NOT:
if (n_ops != 7)
{
ops[n_ops] = constm1_rtx;
negs[n_ops++] = negs[i];
ops[i] = XEXP (ops[i], 0);
negs[i] = ! negs[i];
changed = 1;
}
break;
case CONST_INT:
if (negs[i])
ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0, changed = 1;
break;
default:
break;
}
}
if (n_ops <= 2)
return 0;
changed = 1;
while (changed)
{
changed = first;
for (i = 0; i < n_ops - 1; i++)
for (j = i + 1; j < n_ops; j++)
if (ops[i] != 0 && ops[j] != 0
&& (! first || (CONSTANT_P (ops[i]) && CONSTANT_P (ops[j]))))
{
rtx lhs = ops[i], rhs = ops[j];
enum rtx_code ncode = PLUS;
if (negs[i] && ! negs[j])
lhs = ops[j], rhs = ops[i], ncode = MINUS;
else if (! negs[i] && negs[j])
ncode = MINUS;
tem = simplify_binary_operation (ncode, mode, lhs, rhs);
if (tem)
{
ops[i] = tem, ops[j] = 0;
negs[i] = negs[i] && negs[j];
if (GET_CODE (tem) == NEG)
ops[i] = XEXP (tem, 0), negs[i] = ! negs[i];
if (GET_CODE (ops[i]) == CONST_INT && negs[i])
ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0;
changed = 1;
}
}
first = 0;
}
for (i = 0, j = 0; j < n_ops; j++)
if (ops[j] != 0)
{
ops[i] = ops[j], negs[i++] = negs[j];
if (GET_CODE (ops[j]) == CONST)
n_consts++;
}
if (i + n_consts > input_ops
|| (i + n_consts == input_ops && n_consts <= input_consts))
return 0;
n_ops = i;
for (i = 0; i < n_ops - 1; i++)
if (GET_CODE (ops[i]) == CONST_INT)
{
tem = ops[n_ops - 1], ops[n_ops - 1] = ops[i] , ops[i] = tem;
j = negs[n_ops - 1], negs[n_ops - 1] = negs[i], negs[i] = j;
}
for (i = 0; i < n_ops && negs[i]; i++)
;
if (i == n_ops)
{
for (i = 0; i < n_ops; i++)
negs[i] = 0;
negate = 1;
}
else if (i != 0)
{
tem = ops[0], ops[0] = ops[i], ops[i] = tem;
j = negs[0], negs[0] = negs[i], negs[i] = j;
}
result = ops[0];
for (i = 1; i < n_ops; i++)
result = cse_gen_binary (negs[i] ? MINUS : PLUS, mode, result, ops[i]);
return negate ? gen_rtx_NEG (mode, result) : result;
}
static rtx
cse_gen_binary (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
rtx tem;
if (GET_RTX_CLASS (code) == 'c'
&& ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
|| (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
&& GET_RTX_CLASS (GET_CODE (op1)) != 'o')
|| (GET_CODE (op0) == SUBREG
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
&& GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
tem = op0, op0 = op1, op1 = tem;
tem = simplify_binary_operation (code, mode, op0, op1);
if (tem)
return tem;
if (code == PLUS && GET_CODE (op1) == CONST_INT
&& GET_MODE (op0) != VOIDmode)
return plus_constant (op0, INTVAL (op1));
else if (code == MINUS && GET_CODE (op1) == CONST_INT
&& GET_MODE (op0) != VOIDmode)
return plus_constant (op0, - INTVAL (op1));
else
return gen_rtx_fmt_ee (code, mode, op0, op1);
}
struct cfc_args
{
rtx op0, op1;
int equal, op0lt, op1lt;
};
static void
check_fold_consts (data)
PTR data;
{
struct cfc_args * args = (struct cfc_args *) data;
REAL_VALUE_TYPE d0, d1;
REAL_VALUE_FROM_CONST_DOUBLE (d0, args->op0);
REAL_VALUE_FROM_CONST_DOUBLE (d1, args->op1);
args->equal = REAL_VALUES_EQUAL (d0, d1);
args->op0lt = REAL_VALUES_LESS (d0, d1);
args->op1lt = REAL_VALUES_LESS (d1, d0);
}
rtx
simplify_relational_operation (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
int equal, op0lt, op0ltu, op1lt, op1ltu;
rtx tem;
if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC
#ifdef HAVE_cc0
|| op0 == cc0_rtx
#endif
)
return 0;
if (INTEGRAL_MODE_P (mode) && op1 != const0_rtx
&& ! ((GET_CODE (op0) == REG || GET_CODE (op0) == CONST_INT)
&& (GET_CODE (op1) == REG || GET_CODE (op1) == CONST_INT))
&& 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
&& code != GTU && code != GEU && code != LTU && code != LEU)
return simplify_relational_operation (signed_condition (code),
mode, tem, const0_rtx);
if (rtx_equal_p (op0, op1)
&& (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (GET_MODE (op0)) || flag_fast_math))
equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
else if (GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
{
struct cfc_args args;
args.op0 = op0;
args.op1 = op1;
if (do_float_handler(check_fold_consts, (PTR) &args) == 0)
return 0;
equal = args.equal;
op0lt = op0ltu = args.op0lt;
op1lt = op1ltu = args.op1lt;
}
#endif
else if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
&& (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
&& (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
{
int width = GET_MODE_BITSIZE (mode);
HOST_WIDE_INT l0s, h0s, l1s, h1s;
unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
if (GET_CODE (op0) == CONST_DOUBLE)
{
l0u = l0s = CONST_DOUBLE_LOW (op0);
h0u = h0s = CONST_DOUBLE_HIGH (op0);
}
else
{
l0u = l0s = INTVAL (op0);
h0u = h0s = l0s < 0 ? -1 : 0;
}
if (GET_CODE (op1) == CONST_DOUBLE)
{
l1u = l1s = CONST_DOUBLE_LOW (op1);
h1u = h1s = CONST_DOUBLE_HIGH (op1);
}
else
{
l1u = l1s = INTVAL (op1);
h1u = h1s = l1s < 0 ? -1 : 0;
}
if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
h0u = h1u = 0, h0s = l0s < 0 ? -1 : 0, h1s = l1s < 0 ? -1 : 0;
if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
{
l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
if (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
l0s |= ((HOST_WIDE_INT) (-1) << width);
if (l1s & ((HOST_WIDE_INT) 1 << (width - 1)))
l1s |= ((HOST_WIDE_INT) (-1) << width);
}
equal = (h0u == h1u && l0u == l1u);
op0lt = (h0s < h1s || (h0s == h1s && l0s < l1s));
op1lt = (h1s < h0s || (h1s == h0s && l1s < l0s));
op0ltu = (h0u < h1u || (h0u == h1u && l0u < l1u));
op1ltu = (h1u < h0u || (h1u == h0u && l1u < l0u));
}
else
{
switch (code)
{
case EQ:
if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
|| GET_CODE (op0) == LABEL_REF)
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& op0 != arg_pointer_rtx
#endif
)
return const0_rtx;
break;
case NE:
if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
|| GET_CODE (op0) == LABEL_REF)
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& op0 != arg_pointer_rtx
#endif
)
return const_true_rtx;
break;
case GEU:
if (op1 == const0_rtx)
return const_true_rtx;
break;
case LTU:
if (op1 == const0_rtx)
return const0_rtx;
break;
case LEU:
if (GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == GET_MODE_MASK (mode)
&& INTEGRAL_MODE_P (mode))
return const_true_rtx;
break;
case GTU:
if (GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == GET_MODE_MASK (mode)
&& INTEGRAL_MODE_P (mode))
return const0_rtx;
break;
default:
break;
}
return 0;
}
switch (code)
{
case EQ:
return equal ? const_true_rtx : const0_rtx;
case NE:
return ! equal ? const_true_rtx : const0_rtx;
case LT:
return op0lt ? const_true_rtx : const0_rtx;
case GT:
return op1lt ? const_true_rtx : const0_rtx;
case LTU:
return op0ltu ? const_true_rtx : const0_rtx;
case GTU:
return op1ltu ? const_true_rtx : const0_rtx;
case LE:
return equal || op0lt ? const_true_rtx : const0_rtx;
case GE:
return equal || op1lt ? const_true_rtx : const0_rtx;
case LEU:
return equal || op0ltu ? const_true_rtx : const0_rtx;
case GEU:
return equal || op1ltu ? const_true_rtx : const0_rtx;
default:
abort ();
}
}
rtx
simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
enum rtx_code code;
enum machine_mode mode, op0_mode;
rtx op0, op1, op2;
{
int width = GET_MODE_BITSIZE (mode);
if (width == 0)
width = HOST_BITS_PER_WIDE_INT;
switch (code)
{
case SIGN_EXTRACT:
case ZERO_EXTRACT:
if (GET_CODE (op0) == CONST_INT
&& GET_CODE (op1) == CONST_INT
&& GET_CODE (op2) == CONST_INT
&& INTVAL (op1) + INTVAL (op2) <= GET_MODE_BITSIZE (op0_mode)
&& width <= HOST_BITS_PER_WIDE_INT)
{
HOST_WIDE_INT val = INTVAL (op0);
if (BITS_BIG_ENDIAN)
val >>= (GET_MODE_BITSIZE (op0_mode)
- INTVAL (op2) - INTVAL (op1));
else
val >>= INTVAL (op2);
if (HOST_BITS_PER_WIDE_INT != INTVAL (op1))
{
val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1;
if (code == SIGN_EXTRACT
&& (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1))))
val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1);
}
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
return GEN_INT (val);
}
break;
case IF_THEN_ELSE:
if (GET_CODE (op0) == CONST_INT)
return op0 != const0_rtx ? op1 : op2;
if (GET_CODE (op0) == NE && ! side_effects_p (op0)
&& rtx_equal_p (XEXP (op0, 0), op1)
&& rtx_equal_p (XEXP (op0, 1), op2))
return op1;
else if (GET_CODE (op0) == EQ && ! side_effects_p (op0)
&& rtx_equal_p (XEXP (op0, 1), op1)
&& rtx_equal_p (XEXP (op0, 0), op2))
return op2;
else if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && ! side_effects_p (op0))
{
rtx temp;
temp = simplify_relational_operation (GET_CODE (op0), op0_mode,
XEXP (op0, 0), XEXP (op0, 1));
if (temp == const0_rtx)
return op2;
else if (temp == const1_rtx)
return op1;
}
break;
default:
abort ();
}
return 0;
}
static rtx
fold_rtx (x, insn)
rtx x;
rtx insn;
{
register enum rtx_code code;
register enum machine_mode mode;
register char *fmt;
register 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 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 = 0;
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) == UNITS_PER_WORD
&& GET_MODE (SUBREG_REG (x)) != VOIDmode)
new = operand_subword (folded_arg0, SUBREG_WORD (x), 0,
GET_MODE (SUBREG_REG (x)));
if (new == 0 && subreg_lowpart_p (x))
new = gen_lowpart_if_possible (mode, folded_arg0);
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)
{
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;
case MEM:
if (insn != 0)
find_best_addr (insn, &XEXP (x, 0));
{
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))
&& GET_MODE (addr) == qty_mode[REG_QTY (REGNO (addr))]
&& qty_const[REG_QTY (REGNO (addr))] != 0)
addr = qty_const[REG_QTY (REGNO (addr))];
if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
base = addr;
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
{
base = XEXP (XEXP (addr, 0), 0);
offset = INTVAL (XEXP (XEXP (addr, 0), 1));
}
else if (GET_CODE (addr) == LO_SUM
&& GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
base = XEXP (addr, 1);
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;
}
case ASM_OPERANDS:
for (i = XVECLEN (x, 3) - 1; i >= 0; i--)
validate_change (insn, &XVECEXP (x, 3, i),
fold_rtx (XVECEXP (x, 3, 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;
switch (GET_CODE (arg))
{
case REG:
if (REGNO_QTY_VALID_P (REGNO (arg))
&& qty_const[REG_QTY (REGNO (arg))] != 0
&& GET_CODE (qty_const[REG_QTY (REGNO (arg))]) != REG
&& GET_CODE (qty_const[REG_QTY (REGNO (arg))]) != PLUS)
const_arg
= gen_lowpart_if_possible (GET_MODE (arg),
qty_const[REG_QTY (REGNO (arg))]);
break;
case CONST:
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
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 (const_arg) > COST (folded_arg))
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]
&& COST (replacements[j]) < COST (XEXP (x, i));
j++)
{
if (validate_change (insn, &XEXP (x, i), replacements[j], 0))
break;
if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c')
{
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')
{
if (must_swap || (const_arg0
&& (const_arg1 == 0
|| (GET_CODE (const_arg0) == CONST_INT
&& GET_CODE (const_arg1) != CONST_INT))))
{
register 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 = const_true_rtx, false = const0_rtx;
enum machine_mode mode_arg1;
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
mode);
false = 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;
else if (code == NE)
return true;
}
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode_arg0) || flag_fast_math)
&& (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)
% NBUCKETS), mode_arg0))
&& (p1 = lookup (folded_arg1,
(safe_hash (folded_arg1, mode_arg0)
% NBUCKETS), mode_arg0))
&& p0->first_same_value == p1->first_same_value)))
return ((code == EQ || code == LE || code == GE
|| code == LEU || code == GEU)
? true : false);
else if (GET_CODE (folded_arg0) == REG)
{
int qty = REG_QTY (REGNO (folded_arg0));
if (REGNO_QTY_VALID_P (REGNO (folded_arg0))
&& (comparison_dominates_p (qty_comparison_code[qty], code)
|| (comparison_dominates_p (qty_comparison_code[qty],
reverse_condition (code))
&& ! FLOAT_MODE_P (mode_arg0)))
&& (rtx_equal_p (qty_comparison_const[qty], folded_arg1)
|| (const_arg1
&& rtx_equal_p (qty_comparison_const[qty],
const_arg1))
|| (GET_CODE (folded_arg1) == REG
&& (REG_QTY (REGNO (folded_arg1))
== qty_comparison_qty[qty]))))
return (comparison_dominates_p (qty_comparison_code[qty],
code)
? true : false);
}
}
}
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 = const_true_rtx, false = const0_rtx;
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
mode);
false = CONST0_RTX (mode);
}
#endif
switch (code)
{
case EQ:
return false;
case NE:
return true;
case LT: case LE:
if (has_sign)
return true;
break;
case GT: case GE:
if (has_sign)
return false;
break;
default:
break;
}
}
}
new = simplify_relational_operation (code, mode_arg0,
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)
new = ((new == const0_rtx) ? CONST0_RTX (mode)
: CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE, 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) % NBUCKETS,
mode);
if (p)
for (p = p->first_same_value; p; p = p->next_same_value)
if (GET_CODE (p->exp) == REG)
return cse_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 DIV: case UDIV:
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
= (code == MULT || code == DIV || code == UDIV ? MULT
: is_shift || code == PLUS || 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 cse_gen_binary (code, mode, y, new_const);
}
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))
&& qty_const[REG_QTY (REGNO (x))])
x = gen_lowpart_if_possible (GET_MODE (x), qty_const[REG_QTY (REGNO (x))]);
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)) % NBUCKETS, 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;
register rtx x;
{
rtx result = gen_lowpart_common (mode, x);
if (result)
return result;
else if (GET_CODE (x) == MEM)
{
register 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 = gen_rtx_MEM (mode, plus_constant (XEXP (x, 0), offset));
if (! memory_address_p (mode, XEXP (new, 0)))
return 0;
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
MEM_COPY_ATTRIBUTES (new, x);
return new;
}
else
return 0;
}
static void
record_jump_equiv (insn, taken)
rtx insn;
int taken;
{
int cond_known_true;
rtx op0, op1;
enum machine_mode mode, mode0, mode1;
int reversed_nonequality = 0;
enum rtx_code code;
if (! condjump_p (insn) || simplejump_p (insn))
return;
if (taken)
cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 2) == pc_rtx);
else
cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx);
code = GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0));
op0 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0), insn);
op1 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 1), insn);
code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
if (! cond_known_true)
{
reversed_nonequality = (code != EQ && code != NE);
code = reverse_condition (code);
}
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, op0_in_struct, op1_in_memory, op1_in_struct;
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;
hash_arg_in_struct = 0;
op0_hash = HASH (op0, mode);
op0_in_memory = hash_arg_in_memory;
op0_in_struct = hash_arg_in_struct;
if (do_not_record)
return;
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
op1_hash = HASH (op1, mode);
op1_in_memory = hash_arg_in_memory;
op1_in_struct = hash_arg_in_struct;
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)))
{
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_PTR, 0))
{
rehash_using_reg (op0);
op0_hash = HASH (op0, mode);
if (! CONSTANT_P (op1))
op1_hash = HASH (op1,mode);
}
op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
op0_elt->in_struct = op0_in_struct;
}
qty_comparison_code[REG_QTY (REGNO (op0))] = code;
if (GET_CODE (op1) == REG)
{
op1_elt = lookup (op1, op1_hash, mode);
if (op1_elt == 0)
{
if (insert_regs (op1, NULL_PTR, 0))
{
rehash_using_reg (op1);
op1_hash = HASH (op1, mode);
}
op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
op1_elt->in_struct = op1_in_struct;
}
qty_comparison_qty[REG_QTY (REGNO (op0))] = REG_QTY (REGNO (op1));
qty_comparison_const[REG_QTY (REGNO (op0))] = 0;
}
else
{
qty_comparison_qty[REG_QTY (REGNO (op0))] = -1;
qty_comparison_const[REG_QTY (REGNO (op0))] = op1;
}
return;
}
if (op0_elt == 0)
{
if (insert_regs (op0, NULL_PTR, 0))
{
rehash_using_reg (op0);
op0_hash = HASH (op0, mode);
}
op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
op0_elt->in_struct = op0_in_struct;
}
if (op1_elt == 0)
{
if (insert_regs (op1, NULL_PTR, 0))
{
rehash_using_reg (op1);
op1_hash = HASH (op1, mode);
}
op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
op1_elt->in_struct = op1_in_struct;
}
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;
rtx *inner_dest_loc;
char src_in_memory;
char src_in_struct;
char src_volatile;
enum machine_mode mode;
rtx src_const;
unsigned src_const_hash;
struct table_elt *src_const_elt;
};
static void
cse_insn (insn, libcall_insn)
rtx insn;
rtx libcall_insn;
{
register rtx x = PATTERN (insn);
register int i;
rtx tem;
register 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;
int src_eqv_in_memory;
int src_eqv_in_struct;
unsigned src_eqv_hash;
struct set *sets;
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);
}
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)
{
register int lim = XVECLEN (x, 0);
sets = (struct set *) alloca (lim * sizeof (struct set));
for (i = 0; i < lim; i++)
{
register 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++)
{
register 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 = canon_reg (XEXP (tem, 0), NULL_RTX);
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;
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_n_dups[insn_code] > 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++)
{
register rtx src, dest;
register rtx src_folded;
register 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 = 10000, src_eqv_cost = 10000, src_folded_cost = 10000;
int src_related_cost = 10000, src_elt_cost = 10000;
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;
hash_arg_in_struct = 0;
src_eqv = fold_rtx (src_eqv, insn);
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;
src_eqv_in_struct = hash_arg_in_struct;
}
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;
hash_arg_in_struct = 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_in_struct = hash_arg_in_struct;
if (GET_CODE (src) == MEM
&& find_reg_note (insn, REG_EQUIV, src) != 0
&& GET_CODE (dest) == REG
&& REGNO (dest) >= FIRST_PSEUDO_REGISTER
&& REG_N_SETS (REGNO (dest)) != 1)
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 = -1;
else
src_cost = COST (src);
}
if (src_eqv_here)
{
if (rtx_equal_p (src_eqv_here, dest))
src_eqv_cost = -1;
else
src_eqv_cost = COST (src_eqv_here);
}
if (src_folded)
{
if (rtx_equal_p (src_folded, dest))
src_folded_cost = -1;
else
src_folded_cost = COST (src_folded);
}
if (src_related)
{
if (rtx_equal_p (src_related, dest))
src_related_cost = -1;
else
src_related_cost = COST (src_related);
}
if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF)
src_folded = src_const, src_folded_cost = -1;
while (1)
{
rtx trial, old_src;
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;
if (src_folded_cost <= src_cost
&& src_folded_cost <= src_eqv_cost
&& src_folded_cost <= src_related_cost
&& src_folded_cost <= src_elt_cost)
{
trial = src_folded, src_folded_cost = 10000;
if (src_folded_force_flag)
trial = force_const_mem (mode, trial);
}
else if (src_cost <= src_eqv_cost
&& src_cost <= src_related_cost
&& src_cost <= src_elt_cost)
trial = src, src_cost = 10000;
else if (src_eqv_cost <= src_related_cost
&& src_eqv_cost <= src_elt_cost)
trial = copy_rtx (src_eqv_here), src_eqv_cost = 10000;
else if (src_related_cost <= src_elt_cost)
trial = copy_rtx (src_related), src_related_cost = 10000;
else
{
trial = copy_rtx (elt->exp);
elt = elt->next_same_value;
src_elt_cost = 10000;
}
old_src = SET_SRC (sets[i].rtl);
if (n_sets == 1 && dest == pc_rtx
&& (trial == pc_rtx
|| (GET_CODE (trial) == LABEL_REF
&& ! condjump_p (insn))))
{
if (GET_CODE (trial) == CODE_LABEL
&& NEXT_INSN (trial) != 0
&& GET_CODE (NEXT_INSN (trial)) == JUMP_INSN
&& (GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_DIFF_VEC
|| GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_VEC))
trial = gen_rtx_LABEL_REF (Pmode, get_label_after (trial));
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 (old_src) == REG
|| GET_CODE (old_src) == SUBREG
|| GET_CODE (old_src) == MEM))
replace_rtx (REG_NOTES (libcall_insn), old_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)
&& (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))
&& qty_mode[REG_QTY (REGNO (dest))] == GET_MODE (dest)
&& qty_first_reg[REG_QTY (REGNO (dest))] != REGNO (dest)
&& GET_CODE (src) == REG && REGNO (src) == REGNO (dest)
&& (GET_CODE (sets[i].src) != REG
|| REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER))
{
int first = qty_first_reg[REG_QTY (REGNO (src))];
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)
{
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 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_in_struct = hash_arg_in_struct;
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))
{
tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
src_const = copy_rtx (src_const);
if (tem)
XEXP (tem, 0) = src_const;
else
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL,
src_const, REG_NOTES (insn));
if (REGNO_QTY_VALID_P (REGNO (dest))
&& qty_const[REG_QTY (REGNO (dest))] == const0_rtx)
{
rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
rtx const_insn = qty_const_insn[REG_QTY (REGNO (dest))];
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;
sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl);
while (GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART)
{
sets[i].inner_dest_loc = &XEXP (dest, 0);
dest = XEXP (dest, 0);
}
sets[i].inner_dest = dest;
if (GET_CODE (dest) == MEM)
{
#ifdef PUSH_ROUNDING
rtx addr = XEXP (dest, 0);
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
&& 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)
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
cse_jumps_altered = 1;
if (JUMP_LABEL (insn) != 0)
--LABEL_NUSES (JUMP_LABEL (insn));
sets[i].rtl = 0;
}
else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF)
{
rtx p;
if (! simplejump_p (insn) && n_sets == 1)
{
rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
JUMP_LABEL (new) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
delete_insn (insn);
insn = new;
}
else
INSN_CODE (insn) = -1;
p = insn;
while (p != 0 && NEXT_INSN (p) != 0
&& GET_CODE (NEXT_INSN (p)) != BARRIER
&& GET_CODE (NEXT_INSN (p)) != CODE_LABEL)
{
if (GET_CODE (NEXT_INSN (p)) != NOTE
|| NOTE_LINE_NUMBER (NEXT_INSN (p)) == NOTE_INSN_DELETED)
{
p = delete_insn (NEXT_INSN (p));
if (p)
p = PREV_INSN (p);
}
else
p = NEXT_INSN (p);
}
if (NEXT_INSN (insn) == 0
|| GET_CODE (NEXT_INSN (insn)) != BARRIER)
emit_barrier_before (NEXT_INSN (insn));
if (p != 0 && p != insn && NEXT_INSN (p) != 0
&& GET_CODE (NEXT_INSN (p)) == BARRIER)
delete_insn (NEXT_INSN (p));
cse_jumps_altered = 1;
sets[i].rtl = 0;
}
else if (do_not_record)
{
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == MEM)
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)))
{
register struct table_elt *elt;
register 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;
elt->in_struct = src_eqv_in_struct;
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
{
register struct table_elt *classp = src_eqv_elt;
register rtx src = sets[i].src;
register 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
&& (GET_CODE (src) != REG
|| REGNO (src) >= FIRST_PSEUDO_REGISTER
|| ! find_reg_note (insn, REG_RETVAL, NULL_RTX)))
{
register 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;
elt->in_struct = sets[i].src_in_struct;
sets[i].src_elt = classp = elt;
}
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_CALL_P (insn))
invalidate_memory ();
invalidate_for_call ();
}
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
register rtx dest = SET_DEST (sets[i].rtl);
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == MEM)
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
{
register int regno = REGNO (x);
register int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (x)));
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)
{
register 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)
{
register rtx dest = SET_DEST (sets[i].rtl);
rtx inner_dest = sets[i].inner_dest;
register 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 (elt->in_memory)
{
elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest)
|| sets[i].inner_dest != SET_DEST (sets[i].rtl));
}
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;
if (GET_CODE (elt->exp) != REG
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
continue;
new_src = gen_lowpart_if_possible (new_mode, elt->exp);
if (new_src == 0)
new_src = gen_rtx_SUBREG (new_mode, elt->exp, 0);
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;
src_elt->in_struct = elt->in_struct;
}
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
#ifdef NEXT_SEMANTICS
&& PREV_INSN(insn)
#endif
&& 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)))
&& (qty_first_reg[REG_QTY (REGNO (SET_SRC (sets[0].rtl)))]
== REGNO (SET_DEST (sets[0].rtl)))
&& ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
rtx prev = PREV_INSN (insn);
while (prev && GET_CODE (prev) == NOTE)
prev = PREV_INSN (prev);
if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET
&& SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl))
{
rtx dest = SET_DEST (sets[0].rtl);
rtx note = find_reg_note (prev, REG_EQUIV, NULL_RTX);
validate_change (prev, & SET_DEST (PATTERN (prev)), dest, 1);
validate_change (insn, & SET_DEST (sets[0].rtl),
SET_SRC (sets[0].rtl), 1);
validate_change (insn, & SET_SRC (sets[0].rtl), dest, 1);
apply_change_group ();
if (note)
PUT_REG_NOTE_KIND (note, REG_EQUAL);
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 && reg_mentioned_p (dest, 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))
{
PUT_CODE (prev_insn, NOTE);
NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (prev_insn) = 0;
}
prev_insn_cc0 = this_insn_cc0;
prev_insn_cc0_mode = this_insn_cc0_mode;
#endif
prev_insn = insn;
}
static void
invalidate_memory ()
{
register int i;
register struct table_elt *p, *next;
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory)
remove_from_table (p, i);
}
}
static int
note_mem_written (addr)
register rtx addr;
{
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
&& 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)++;
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)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
register 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);
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 PC:
case CC0:
case LO_SUM:
return x;
case MEM:
XEXP (x, 0) = cse_process_notes (XEXP (x, 0), x);
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))
&& qty_const[i] != 0
&& (CONSTANT_P (qty_const[i])
|| GET_CODE (qty_const[i]) == REG))
{
rtx new = gen_lowpart_if_possible (GET_MODE (x), qty_const[i]);
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 (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER))
cse_set_around_loop (PATTERN (insn), insn, loop_start);
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& 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)
rtx set;
rtx dest;
{
enum rtx_code code = GET_CODE (dest);
if (code == MEM
&& ! note_mem_written (dest)
&& (MEM_IN_STRUCT_P (dest) || GET_MODE (dest) == BLKmode
|| cse_rtx_varies_p (XEXP (dest, 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 (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (GET_CODE (insn) == CALL_INSN)
{
if (! CONST_CALL_P (insn))
invalidate_memory ();
invalidate_for_call ();
}
invalidate_from_clobbers (PATTERN (insn));
note_stores (PATTERN (insn), invalidate_skipped_set);
}
}
static rtx cse_check_loop_start_value;
static void
cse_check_loop_start (x, set)
rtx x;
rtx set ATTRIBUTE_UNUSED;
{
if (cse_check_loop_start_value == 0
|| 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 = 0;
}
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;
cse_check_loop_start_value = SET_SRC (x);
for (q = p; q != loop_start; q = NEXT_INSN (q))
if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
note_stores (PATTERN (q), cse_check_loop_start);
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;
}
}
}
note_mem_written (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 = GET_RTX_CLASS (GET_CODE (insn)) == 'i' ? 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--;
}
while (p && GET_CODE (p) != CODE_LABEL)
{
if (! after_loop && GET_CODE (p) == NOTE
&& NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
break;
if (GET_CODE (p) == NOTE
&& NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP)
break;
if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& 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
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_SETJMP)
&& (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)
{
register 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;
register rtx insn = f;
register 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_next_eqv = (int *) alloca (nregs * sizeof (int));
reg_prev_eqv = (int *) alloca (nregs * sizeof (int));
#ifdef LOAD_EXTEND_OP
memory_extend_rtx = gen_rtx_ZERO_EXTEND (VOIDmode, NULL_RTX);
#endif
bzero ((char *) table, sizeof table);
free_element_chain = 0;
n_elements_made = 0;
max_uid = get_max_uid ();
uid_cuid = (int *) alloca ((max_uid + 1) * sizeof (int));
bzero ((char *) uid_cuid, (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;
}
CLEAR_HARD_REG_SET (regs_invalidated_by_call);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if ((call_used_regs[i]
&& i != STACK_POINTER_REGNUM
&& i != FRAME_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& i != HARD_FRAME_POINTER_REGNUM
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
#endif
#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
&& ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
#endif
)
|| global_regs[i])
SET_HARD_REG_BIT (regs_invalidated_by_call, i);
insn = f;
while (insn)
{
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;
}
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
qty_const = 0;
if (max_elements_made < n_elements_made)
max_elements_made = n_elements_made;
return cse_jumps_altered || recorded_label_ref;
}
static rtx
cse_basic_block (from, to, next_branch, around_loop)
register rtx from, to;
struct branch_path *next_branch;
int around_loop;
{
register rtx insn;
int to_usage = 0;
rtx libcall_insn = NULL_RTX;
int num_insns = 0;
qty_first_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int));
qty_last_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int));
qty_mode= (enum machine_mode *) alloca ((max_qty - max_reg) * sizeof (enum machine_mode));
qty_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
qty_const_insn = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
qty_comparison_code
= (enum rtx_code *) alloca ((max_qty - max_reg) * sizeof (enum rtx_code));
qty_comparison_qty = (int *) alloca ((max_qty - max_reg) * sizeof (int));
qty_comparison_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
qty_first_reg -= max_reg;
qty_last_reg -= max_reg;
qty_mode -= max_reg;
qty_const -= max_reg;
qty_const_insn -= max_reg;
qty_comparison_code -= max_reg;
qty_comparison_qty -= max_reg;
qty_comparison_const -= 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))
{
register 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 ((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 = NULL_RTX;
cse_insn (insn, libcall_insn);
}
if (simplejump_p (insn))
{
if (to == 0)
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 (LABEL_NUSES (to) == 0)
insn = delete_insn (to);
if (insn == 0)
return 0;
prev = prev_nonnote_insn (to);
if (prev && GET_CODE (prev) == BARRIER)
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 ();
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 (PREV_INSN (to)) == JUMP_INSN
&& JUMP_LABEL (PREV_INSN (to)) != 0
&& LABEL_NUSES (JUMP_LABEL (PREV_INSN (to))) == 1)
cse_around_loop (JUMP_LABEL (PREV_INSN (to)));
return to ? NEXT_INSN (to) : 0;
}
static void
count_reg_usage (x, counts, dest, incr)
rtx x;
int *counts;
rtx dest;
int incr;
{
enum rtx_code code;
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 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,
side_effects_p (SET_SRC (x)) ? NULL_RTX : 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);
count_reg_usage (REG_NOTES (x), counts, NULL_RTX, incr);
return;
case EXPR_LIST:
case INSN_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL
|| (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE))
count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
return;
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);
}
}
void
delete_trivially_dead_insns (insns, nreg)
rtx insns;
int nreg;
{
int *counts = (int *) alloca (nreg * sizeof (int));
rtx insn, prev;
#ifdef HAVE_cc0
rtx tem;
#endif
int i;
int in_libcall = 0, dead_libcall = 0;
bzero ((char *) counts, sizeof (int) * nreg);
for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
count_reg_usage (insn, counts, NULL_RTX, 1);
for (insn = prev_real_insn (get_last_insn ()); insn; insn = prev)
{
int live_insn = 0;
rtx note;
prev = prev_real_insn (insn);
if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
in_libcall = 1;
live_insn = 1;
dead_libcall = 0;
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (note)
{
rtx set = single_set (insn);
if (set
&& validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0))
{
remove_note (insn,
find_reg_note (insn, REG_RETVAL, NULL_RTX));
dead_libcall = 1;
}
}
}
else if (in_libcall)
live_insn = ! dead_libcall;
else if (GET_CODE (PATTERN (insn)) == SET)
{
if (GET_CODE (SET_DEST (PATTERN (insn))) == REG
&& SET_DEST (PATTERN (insn)) == SET_SRC (PATTERN (insn)))
;
#ifdef HAVE_cc0
else if (GET_CODE (SET_DEST (PATTERN (insn))) == CC0
&& ! side_effects_p (SET_SRC (PATTERN (insn)))
&& ((tem = next_nonnote_insn (insn)) == 0
|| GET_RTX_CLASS (GET_CODE (tem)) != 'i'
|| ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
;
#endif
else if (GET_CODE (SET_DEST (PATTERN (insn))) != REG
|| REGNO (SET_DEST (PATTERN (insn))) < FIRST_PSEUDO_REGISTER
|| counts[REGNO (SET_DEST (PATTERN (insn)))] != 0
|| side_effects_p (SET_SRC (PATTERN (insn))))
live_insn = 1;
}
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 (GET_CODE (SET_DEST (elt)) == REG
&& SET_DEST (elt) == SET_SRC (elt))
;
#ifdef HAVE_cc0
else if (GET_CODE (SET_DEST (elt)) == CC0
&& ! side_effects_p (SET_SRC (elt))
&& ((tem = next_nonnote_insn (insn)) == 0
|| GET_RTX_CLASS (GET_CODE (tem)) != 'i'
|| ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
;
#endif
else if (GET_CODE (SET_DEST (elt)) != REG
|| REGNO (SET_DEST (elt)) < FIRST_PSEUDO_REGISTER
|| counts[REGNO (SET_DEST (elt))] != 0
|| side_effects_p (SET_SRC (elt)))
live_insn = 1;
}
else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
live_insn = 1;
}
else
live_insn = 1;
if (! live_insn)
{
count_reg_usage (insn, counts, NULL_RTX, -1);
delete_insn (insn);
}
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
{
in_libcall = 0;
dead_libcall = 0;
}
}
}