/* Common subexpression elimination for GNU compiler.
- Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
This file is part of GNU CC.
static int do_not_record;
+#ifdef LOAD_EXTEND_OP
+
+/* Scratch rtl used when looking for load-extended copy of a MEM. */
+static rtx memory_extend_rtx;
+#endif
+
/* canon_hash stores 1 in hash_arg_in_memory
if it notices a reference to memory within the expression being hashed. */
char flag;
};
-#define HASHBITS 16
-
/* We don't want a lot of buckets, because we rarely have very many
things stored in the hash table, and a lot of buckets slows
down a lot of loops that happen frequently. */
#define HASH(X, M) \
(GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \
- ? ((((int) REG << 7) + reg_qty[REGNO (X)]) % NBUCKETS) \
+ ? (((unsigned) REG << 7) + (unsigned) reg_qty[REGNO (X)]) % NBUCKETS \
: canon_hash (X, M) % NBUCKETS)
/* Determine whether register number N is considered a fixed register for CSE.
#ifdef OVERLAPPING_REGNO_P
#define FIXED_REGNO_P(N) \
(((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
- || fixed_regs[N]) \
+ || 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])
+ || fixed_regs[N] || global_regs[N])
#endif
/* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed
of 0. Next come pseudos with a cost of one and other hard registers with
a cost of 2. Aside from these special cases, call `rtx_cost'. */
-#define CHEAP_REG(N) \
+#define CHEAP_REGNO(N) \
((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM \
|| ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \
|| ((N) < FIRST_PSEUDO_REGISTER \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
+/* A register is cheap if it is a user variable assigned to the register
+ or if its register number always corresponds to a cheap register. */
+
+#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 (REGNO (X)) ? 0 \
+ ? (CHEAP_REG (X) ? 0 \
: REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1 \
: 2) \
: rtx_cost (X, SET) * 2)
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 *, int));
+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, int, enum machine_mode)),
- *lookup_for_remove PROTO((rtx, int, enum machine_mode));
+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 *, int,
+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));
+static void invalidate PROTO((rtx, enum machine_mode));
static void remove_invalid_refs PROTO((int));
static void rehash_using_reg PROTO((rtx));
static void invalidate_memory PROTO((struct write_data *));
static void invalidate_for_call PROTO((void));
static rtx use_related_value PROTO((rtx, struct table_elt *));
-static int canon_hash PROTO((rtx, enum machine_mode));
-static int safe_hash PROTO((rtx, enum machine_mode));
+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 *,
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 *, int));
+static void count_reg_usage PROTO((rtx, int *, rtx, int));
+
+extern int rtx_equal_function_value_matters;
\f
/* Return an estimate of the cost of computing rtx X.
One use is in cse, to decide which expression to keep in the hash table.
switch (code)
{
case REG:
- return ! CHEAP_REG (REGNO (x));
+ return ! CHEAP_REG (x);
case SUBREG:
/* If we can't tie these modes, make this expensive. The larger
next_qty = max_reg;
- bzero (reg_tick, max_reg * sizeof (int));
+ bzero ((char *) reg_tick, max_reg * sizeof (int));
- bcopy (all_minus_one, reg_in_table, max_reg * sizeof (int));
- bcopy (consec_ints, reg_qty, max_reg * sizeof (int));
+ bcopy ((char *) all_minus_one, (char *) reg_in_table,
+ max_reg * sizeof (int));
+ bcopy ((char *) consec_ints, (char *) reg_qty, max_reg * sizeof (int));
CLEAR_HARD_REG_SET (hard_regs_in_table);
/* The per-quantity values used to be initialized here, but it is
}
}
- bzero (table, sizeof table);
+ bzero ((char *) table, sizeof table);
prev_insn = 0;
delete_reg_equiv (reg)
register int reg;
{
- register int n = reg_next_eqv[reg];
- register int p = reg_prev_eqv[reg];
register int q = reg_qty[reg];
+ register int p, n;
- /* If invalid, do nothing. N and P above are undefined in that case. */
+ /* If invalid, do nothing. */
if (q == reg)
return;
+ p = reg_prev_eqv[reg];
+ n = reg_next_eqv[reg];
+
if (n != -1)
reg_prev_eqv[n] = p;
else
static void
remove_from_table (elt, hash)
register struct table_elt *elt;
- int hash;
+ unsigned hash;
{
if (elt == 0)
return;
static struct table_elt *
lookup (x, hash, mode)
rtx x;
- int hash;
+ unsigned hash;
enum machine_mode mode;
{
register struct table_elt *p;
static struct table_elt *
lookup_for_remove (x, hash, mode)
rtx x;
- int hash;
+ unsigned hash;
enum machine_mode mode;
{
register struct table_elt *p;
insert (x, classp, hash, mode)
register rtx x;
register struct table_elt *classp;
- int hash;
+ unsigned hash;
enum machine_mode mode;
{
register struct table_elt *elt;
if (GET_CODE (x) == CONST)
{
rtx subexp = get_related_value (x);
- int subhash;
+ unsigned subhash;
struct table_elt *subelt, *subelt_prev;
if (subexp != 0)
for (elt = class2; elt; elt = next)
{
- int hash;
+ unsigned hash;
rtx exp = elt->exp;
enum machine_mode mode = elt->mode;
(because, when a memory reference with a varying address is stored in,
all memory references are removed by invalidate_memory
so specific invalidation is superfluous).
+ FULL_MODE, if not VOIDmode, indicates that this much should be invalidated
+ instead of just the amount indicated by the mode of X. This is only used
+ for bitfield stores into memory.
A nonvarying address may be just a register or just
a symbol reference, or it may be either of those plus
a numeric offset. */
static void
-invalidate (x)
+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 int hash = HASH (x, GET_MODE (x));
+ register unsigned hash = HASH (x, GET_MODE (x));
/* Remove REGNO from any quantity list it might be on and indicate
that it's value might have changed. If it is a pseudo, remove its
{
if (GET_CODE (SUBREG_REG (x)) != REG)
abort ();
- invalidate (SUBREG_REG (x));
+ invalidate (SUBREG_REG (x), VOIDmode);
return;
}
if (GET_CODE (x) != MEM)
abort ();
- set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (GET_MODE (x)),
+ if (full_mode == VOIDmode)
+ full_mode = GET_MODE (x);
+
+ set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (full_mode),
&base, &start, &end);
for (i = 0; i < NBUCKETS; i++)
{
int i;
struct table_elt *p, *next;
- int hash;
+ unsigned hash;
if (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
{
int regno, endregno;
int i;
- int hash;
+ unsigned hash;
struct table_elt *p, *next;
int in_table = 0;
if (reg_tick[regno] >= 0)
reg_tick[regno]++;
- in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+ in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
}
/* In the case where we have no call-clobbered hard registers in the
Note that cse_insn knows that the hash code of a MEM expression
is just (int) MEM plus the hash code of the address. */
-static int
+static unsigned
canon_hash (x, mode)
rtx x;
enum machine_mode mode;
{
register int i, j;
- register int hash = 0;
+ register unsigned hash = 0;
register enum rtx_code code;
register char *fmt;
do_not_record = 1;
return 0;
}
- return hash + ((int) REG << 7) + reg_qty[regno];
+ hash += ((unsigned) REG << 7) + (unsigned) reg_qty[regno];
+ return hash;
}
case CONST_INT:
- hash += ((int) mode + ((int) CONST_INT << 7)
- + INTVAL (x) + (INTVAL (x) >> HASHBITS));
- return ((1 << HASHBITS) - 1) & hash;
+ {
+ unsigned HOST_WIDE_INT tem = INTVAL (x);
+ hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
+ return hash;
+ }
case CONST_DOUBLE:
/* This is like the general case, except that it only counts
the integers representing the constant. */
- hash += (int) code + (int) GET_MODE (x);
- {
- int i;
- for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
- {
- int tem = XINT (x, i);
- hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
- }
- }
+ hash += (unsigned) code + (unsigned) GET_MODE (x);
+ for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
+ {
+ unsigned tem = XINT (x, i);
+ hash += tem;
+ }
return hash;
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
- /* Use `and' to ensure a positive number. */
- return (hash + ((HOST_WIDE_INT) LABEL_REF << 7)
- + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1)));
+ hash
+ += ((unsigned) LABEL_REF << 7) + (unsigned HOST_WIDE_INT) XEXP (x, 0);
+ return hash;
case SYMBOL_REF:
- return (hash + ((HOST_WIDE_INT) SYMBOL_REF << 7)
- + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1)));
+ hash
+ += ((unsigned) SYMBOL_REF << 7) + (unsigned HOST_WIDE_INT) XSTR (x, 0);
+ return hash;
case MEM:
if (MEM_VOLATILE_P (x))
}
/* Now that we have already found this special case,
might as well speed it up as much as possible. */
- hash += (int) MEM;
+ hash += (unsigned) MEM;
x = XEXP (x, 0);
goto repeat;
}
i = GET_RTX_LENGTH (code) - 1;
- hash += (int) code + (int) GET_MODE (x);
+ hash += (unsigned) code + (unsigned) GET_MODE (x);
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
hash += canon_hash (XVECEXP (x, i, j), 0);
else if (fmt[i] == 's')
{
- register char *p = XSTR (x, i);
+ register unsigned char *p = (unsigned char *) XSTR (x, i);
if (p)
while (*p)
- {
- register int tem = *p++;
- hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
- }
+ hash += *p++;
}
else if (fmt[i] == 'i')
{
- register int tem = XINT (x, i);
- hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
+ register unsigned tem = XINT (x, i);
+ hash += tem;
}
else
abort ();
/* Like canon_hash but with no side effects. */
-static int
+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;
- int hash = canon_hash (x, mode);
+ 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 INTVAL (x) == INTVAL (y);
case LABEL_REF:
- case SYMBOL_REF:
return XEXP (x, 0) == XEXP (y, 0);
+ case SYMBOL_REF:
+ return XSTR (x, 0) == XSTR (y, 0);
+
case REG:
{
int regno = REGNO (y);
set PBASE, PSTART, and PEND which correspond to the base of the address,
the starting offset, and ending offset respectively.
- ADDR is known to be a nonvarying address.
+ ADDR is known to be a nonvarying address. */
- cse_address_varies_p returns zero for nonvarying addresses. */
+/* ??? Despite what the comments say, this function is in fact frequently
+ passed varying addresses. This does not appear to cause any problems. */
static void
set_nonvarying_address_components (addr, size, pbase, pstart, pend)
HOST_WIDE_INT *pstart, *pend;
{
rtx base;
- int start, end;
+ HOST_WIDE_INT start, end;
base = addr;
start = 0;
base = qty_const[reg_qty[REGNO (XEXP (base, 0))]];
}
- /* By definition, operand1 of a LO_SUM is the associated constant
- address. Use the associated constant address as the base instead. */
- if (GET_CODE (base) == LO_SUM)
- base = XEXP (base, 1);
+ /* Handle everything that we can find inside an address that has been
+ viewed as constant. */
+
+ while (1)
+ {
+ /* If no part of this switch does a "continue", the code outside
+ will exit this loop. */
+
+ switch (GET_CODE (base))
+ {
+ case LO_SUM:
+ /* By definition, operand1 of a LO_SUM is the associated constant
+ address. Use the associated constant address as the base
+ instead. */
+ base = XEXP (base, 1);
+ continue;
- /* Strip off CONST. */
- if (GET_CODE (base) == CONST)
- base = XEXP (base, 0);
+ case CONST:
+ /* Strip off CONST. */
+ base = XEXP (base, 0);
+ continue;
- if (GET_CODE (base) == PLUS
- && GET_CODE (XEXP (base, 1)) == CONST_INT)
+ case PLUS:
+ if (GET_CODE (XEXP (base, 1)) == CONST_INT)
+ {
+ start += INTVAL (XEXP (base, 1));
+ base = XEXP (base, 0);
+ continue;
+ }
+ break;
+
+ case AND:
+ /* Handle the case of an AND which is the negative of a power of
+ two. This is used to represent unaligned memory operations. */
+ 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);
+
+ /* Assume the worst misalignment. START is affected, but not
+ END, so compensate but adjusting SIZE. Don't lose any
+ constant we already had. */
+
+ size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
+ start += *pstart - INTVAL (XEXP (base, 1)) - 1;
+ base = *pbase;
+ }
+ break;
+ }
+
+ break;
+ }
+
+ if (GET_CODE (base) == CONST_INT)
{
- start += INTVAL (XEXP (base, 1));
- base = XEXP (base, 0);
+ start += INTVAL (base);
+ base = const0_rtx;
}
end = start + size;
register enum rtx_code code;
register char *fmt;
- if (GET_CODE (base) == CONST_INT)
- {
- start += INTVAL (base);
- end += INTVAL (base);
- base = const0_rtx;
- }
-
repeat:
if (x == 0)
return 0;
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 hash_code;
int addr_volatile;
int regno;
+ unsigned hash;
/* Do not try to replace constant addresses or addresses of local and
argument slots. These MEM expressions are made only once and inserted
of the whole address. Also, ignore if volatile. */
do_not_record = 0;
- hash_code = HASH (addr, Pmode);
+ hash = HASH (addr, Pmode);
addr_volatile = do_not_record;
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
if (addr_volatile)
return;
- elt = lookup (addr, hash_code, Pmode);
+ elt = lookup (addr, hash, Pmode);
#ifndef ADDRESS_COST
if (elt)
rtx c = XEXP (*loc, 1);
do_not_record = 0;
- hash_code = HASH (XEXP (*loc, 0), Pmode);
+ 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_code, Pmode);
+ elt = lookup (XEXP (*loc, 0), hash, Pmode);
if (elt == 0)
return;
int best_rtx_cost = (COST (*loc) + 1) >> 1;
struct table_elt *best_elt = elt;
rtx best_rtx = *loc;
+ int count;
+
+ /* This is at worst case an O(n^2) algorithm, so limit our search
+ to the first 32 elements on the list. This avoids trouble
+ compiling code with very long basic blocks that can easily
+ call cse_gen_binary so many times that we run out of memory. */
found_better = 0;
- for (p = elt->first_same_value; p; p = p->next_same_value)
+ 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)))
check the wrong mode (input vs. output) for a conversion operation,
such as FIX. At some point, this should be simplified. */
-#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
- if (code == FLOAT && GET_CODE (op) == CONST_INT)
- {
- REAL_VALUE_TYPE d;
+#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
-#ifdef REAL_ARITHMETIC
- REAL_VALUE_FROM_INT (d, INTVAL (op), INTVAL (op) < 0 ? ~0 : 0);
-#else
- d = (double) INTVAL (op);
-#endif
- return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
- }
- else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_INT)
+ 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;
-#ifdef REAL_ARITHMETIC
- REAL_VALUE_FROM_INT (d, INTVAL (op), 0);
-#else
- d = (double) (unsigned int) INTVAL (op);
-#endif
- return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
- }
-
- else if (code == FLOAT && GET_CODE (op) == CONST_DOUBLE
- && GET_MODE (op) == VOIDmode)
- {
- 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, CONST_DOUBLE_LOW (op), CONST_DOUBLE_HIGH (op));
+ REAL_VALUE_FROM_INT (d, lv, hv);
#else
- if (CONST_DOUBLE_HIGH (op) < 0)
+ if (hv < 0)
{
- d = (double) (~ CONST_DOUBLE_HIGH (op));
+ 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) (~ CONST_DOUBLE_LOW (op));
+ d += (double) (unsigned HOST_WIDE_INT) (~ lv);
d = (- d - 1.0);
}
else
{
- d = (double) CONST_DOUBLE_HIGH (op);
+ 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) CONST_DOUBLE_LOW (op);
+ d += (double) (unsigned HOST_WIDE_INT) lv;
}
#endif /* REAL_ARITHMETIC */
+
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
- else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_DOUBLE
- && GET_MODE (op) == VOIDmode)
+ 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 (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, CONST_DOUBLE_LOW (op),
- CONST_DOUBLE_HIGH (op));
+ REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv);
#else
- d = (double) CONST_DOUBLE_HIGH (op);
+
+ 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) CONST_DOUBLE_LOW (op);
+ d += (double) (unsigned HOST_WIDE_INT) lv;
#endif /* REAL_ARITHMETIC */
+
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
#endif
/* We can do some operations on integer CONST_DOUBLEs. Also allow
for a DImode operation on a CONST_INT. */
- else if (GET_MODE (op) == VOIDmode
+ 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;
break;
case TRUNCATE:
- if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
- return GEN_INT (l1 & GET_MODE_MASK (mode));
- else
- return 0;
+ /* This is just a change-of-mode, so do nothing. */
break;
case ZERO_EXTEND:
abort ();
}
- x = immed_real_const_1 (d, mode);
+ 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 (mode) == MODE_INT
+
+ 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;
}
#endif
- set_float_handler (NULL_PTR);
value = real_value_truncate (mode, value);
- return immed_real_const_1 (value, mode);
+ set_float_handler (NULL_PTR);
+ return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
}
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
/* We can fold some multi-word operations. */
if (GET_MODE_CLASS (mode) == MODE_INT
- && GET_CODE (op0) == CONST_DOUBLE
+ && 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;
- l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
+ 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);
break;
case LSHIFTRT: case ASHIFTRT:
- case ASHIFT: case LSHIFT:
+ case ASHIFT:
case ROTATE: case ROTATERT:
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
if (code == LSHIFTRT || code == ASHIFTRT)
rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
code == ASHIFTRT);
- else if (code == ASHIFT || code == LSHIFT)
- lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
- code == ASHIFT);
+ 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 /* code == ROTATERT */
/* In IEEE floating point, x+0 is not the same as x. Similarly
for the other optimizations below. */
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
- && FLOAT_MODE_P (mode))
+ && FLOAT_MODE_P (mode) && ! flag_fast_math)
break;
if (op1 == CONST0_RTX (mode))
/* Handle both-operands-constant cases. We can only add
CONST_INTs to constants since the sum of relocatable symbols
- can't be handled by most assemblers. */
+ can't be handled by most assemblers. Don't add CONST_INT
+ to CONST_INT since overflow won't be computed properly if wider
+ than HOST_BITS_PER_WIDE_INT. */
- if (CONSTANT_P (op0) && GET_CODE (op1) == CONST_INT)
+ 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_CODE (op0) == CONST_INT)
+ else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode
+ && GET_CODE (op0) == CONST_INT)
return plus_constant (op1, INTVAL (op0));
+ /* See if this is something like X * C - X or vice versa or
+ if the multiplication is written as a shift. If so, we can
+ distribute and make a new multiply, shift, or maybe just
+ have X (if C is 2 in the example above). But don't make
+ real multiply if we didn't have one before. */
+
+ 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 one of the operands is a PLUS or a MINUS, see if we can
simplify this by the associative law.
Don't use the associative law for floating point.
In IEEE floating point, x-0 is not the same as x. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
- || ! FLOAT_MODE_P (mode))
+ || ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op1 == CONST0_RTX (mode))
return op0;
#else
/* None of these optimizations can be done for IEEE
floating point. */
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
- && FLOAT_MODE_P (mode))
+ && FLOAT_MODE_P (mode) && ! flag_fast_math)
break;
- /* We can't assume x-x is 0 even with non-IEEE floating point. */
+ /* We can't assume x-x is 0 even with non-IEEE floating point,
+ but since it is zero except in very strange circumstances, we
+ will treat it as zero with -ffast-math. */
if (rtx_equal_p (op0, op1)
&& ! side_effects_p (op0)
- && ! FLOAT_MODE_P (mode))
- return const0_rtx;
+ && (! FLOAT_MODE_P (mode) || flag_fast_math))
+ return CONST0_RTX (mode);
/* Change subtraction from zero into negation. */
if (op0 == CONST0_RTX (mode))
if (op1 == CONST0_RTX (mode))
return op0;
+ /* See if this is something like X * C - X or vice versa or
+ if the multiplication is written as a shift. If so, we can
+ distribute and make a new multiply, shift, or maybe just
+ have X (if C is 2 in the example above). But don't make
+ real multiply if we didn't have one before. */
+
+ 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;
+ }
+ }
+
/* (a - (-b)) -> (a + b). */
if (GET_CODE (op1) == NEG)
return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
return tem;
/* Don't let a relocatable value get a negative coeff. */
- if (GET_CODE (op1) == CONST_INT)
+ if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
return plus_constant (op0, - INTVAL (op1));
break;
/* In IEEE floating point, x*0 is not always 0. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
- && ! FLOAT_MODE_P (mode))
+ || ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op1 == CONST0_RTX (mode)
&& ! side_effects_p (op0))
return op1;
if (op1 == CONST1_RTX (mode))
return op0;
- /* Convert multiply by constant power of two into shift. */
+ /* Convert multiply by constant power of two into shift unless
+ we are still generating RTL. This test is a kludge. */
if (GET_CODE (op1) == CONST_INT
- && (val = exact_log2 (INTVAL (op1))) >= 0)
+ && (val = exact_log2 (INTVAL (op1))) >= 0
+ && ! rtx_equal_function_value_matters)
return gen_rtx (ASHIFT, mode, op0, GEN_INT (val));
if (GET_CODE (op1) == CONST_DOUBLE
/* In IEEE floating point, 0/x is not always 0. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
- || ! FLOAT_MODE_P (mode))
+ || ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op0 == CONST0_RTX (mode)
&& ! side_effects_p (op1))
return op0;
-#if 0 /* Turned off till an expert says this is a safe thing to do. */
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
- /* Change division by a constant into multiplication. */
+ /* Change division by a constant into multiplication. Only do
+ this with -ffast-math until an expert says it is safe in
+ general. */
else if (GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT
- && op1 != CONST0_RTX (mode))
+ && op1 != CONST0_RTX (mode)
+ && flag_fast_math)
{
REAL_VALUE_TYPE d;
REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
- if (REAL_VALUES_EQUAL (d, dconst0))
- abort();
+
+ if (! REAL_VALUES_EQUAL (d, dconst0))
+ {
#if defined (REAL_ARITHMETIC)
- REAL_ARITHMETIC (d, (int) RDIV_EXPR, dconst1, d);
- return gen_rtx (MULT, mode, op0,
- CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
+ 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
+ return gen_rtx (MULT, mode, op0,
+ CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
#endif
+ }
+ }
#endif
break;
/* ... fall through ... */
- case LSHIFT:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
- arg1 &= (BITS_PER_WORD - 1);
+ arg1 %= width;
#endif
- if (arg1 >= width)
- return 0;
-
val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
break;
case ASHIFT:
- case LSHIFT:
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
- arg1 &= (BITS_PER_WORD - 1);
+ arg1 %= width;
#endif
- if (arg1 >= width)
- return 0;
-
val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
break;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
- arg1 &= (BITS_PER_WORD - 1);
+ arg1 %= width;
#endif
- if (arg1 >= width)
- return 0;
-
val = arg0s >> arg1;
/* Bootstrap compiler may not have sign extended the right shift.
int first = 1, negate = 0, changed;
int i, j;
- bzero (ops, sizeof ops);
+ bzero ((char *) ops, sizeof ops);
/* Set up the two operands and then expand them until nothing has been
changed. If we run out of room in our array, give up; this should
}
\f
/* Like simplify_binary_operation except used for relational operators.
- MODE is the mode of the operands, not that of the result. */
+ MODE is the mode of the operands, not that of the result. If MODE
+ is VOIDmode, both operands must also be VOIDmode and we compare the
+ operands in "infinite precision".
+
+ If no simplification is possible, this function returns zero. Otherwise,
+ it returns either const_true_rtx or const0_rtx. */
rtx
simplify_relational_operation (code, mode, op0, op1)
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);
+ int equal, op0lt, op0ltu, op1lt, op1ltu;
+ rtx tem;
/* If op0 is a compare, extract the comparison arguments from it. */
if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
- /* What to do with MODE_CC isn't clear yet.
- Let's make sure nothing erroneous is done. */
- if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
+ /* We can't simplify MODE_CC values since we don't know what the
+ actual comparison is. */
+ if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC
+#ifdef HAVE_cc0
+ || op0 == cc0_rtx
+#endif
+ )
return 0;
- /* Unlike the arithmetic operations, we can do the comparison whether
- or not WIDTH is larger than HOST_BITS_PER_WIDE_INT because the
- CONST_INTs are to be understood as being infinite precision as
- is the comparison. So there is no question of overflow. */
-
- if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT || width == 0)
- {
- /* Even if we can't compute a constant result,
- there are some cases worth simplifying. */
-
- /* For non-IEEE floating-point, if the two operands are equal, we know
- the result. */
- if (rtx_equal_p (op0, op1)
- && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
- || ! FLOAT_MODE_P (GET_MODE (op0))))
- return (code == EQ || code == GE || code == LE || code == LEU
- || code == GEU) ? const_true_rtx : const0_rtx;
-
+ /* For integer comparisons of A and B maybe we can simplify A - B and can
+ then simplify a comparison of that with zero. If A and B are both either
+ a register or a CONST_INT, this can't help; testing for these cases will
+ prevent infinite recursion here and speed things up.
+
+ If CODE is an unsigned comparison, we can only do this if A - B is a
+ constant integer, and then we have to compare that integer with zero as a
+ signed comparison. Note that this will give the incorrect result from
+ comparisons that overflow. Since these are undefined, this is probably
+ OK. If it causes a problem, we can check for A or B being an address
+ (fp + const or SYMBOL_REF) and only do it in that case. */
+
+ 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))
+ && (GET_CODE (tem) == CONST_INT
+ || (code != GTU && code != GEU &&
+ code != LTU && code != LEU)))
+ return simplify_relational_operation (signed_condition (code),
+ mode, tem, const0_rtx);
+
+ /* For non-IEEE floating-point, if the two operands are equal, we know the
+ result. */
+ 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 the operands are floating-point constants, see if we can fold
+ the result. */
#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)
- {
- REAL_VALUE_TYPE d0, d1;
- jmp_buf handler;
- int op0lt, op1lt, equal;
+ else if (GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d0, d1;
+ jmp_buf handler;
+
+ if (setjmp (handler))
+ return 0;
- if (setjmp (handler))
- return 0;
+ set_float_handler (handler);
+ REAL_VALUE_FROM_CONST_DOUBLE (d0, op0);
+ REAL_VALUE_FROM_CONST_DOUBLE (d1, op1);
+ equal = REAL_VALUES_EQUAL (d0, d1);
+ op0lt = op0ltu = REAL_VALUES_LESS (d0, d1);
+ op1lt = op1ltu = REAL_VALUES_LESS (d1, d0);
+ set_float_handler (NULL_PTR);
+ }
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
- set_float_handler (handler);
- REAL_VALUE_FROM_CONST_DOUBLE (d0, op0);
- REAL_VALUE_FROM_CONST_DOUBLE (d1, op1);
- equal = REAL_VALUES_EQUAL (d0, d1);
- op0lt = REAL_VALUES_LESS (d0, d1);
- op1lt = REAL_VALUES_LESS (d1, d0);
- set_float_handler (NULL_PTR);
+ /* Otherwise, see if the operands are both integers. */
+ 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;
- switch (code)
- {
- case EQ:
- return equal ? const_true_rtx : const0_rtx;
- case NE:
- return !equal ? const_true_rtx : const0_rtx;
- case LE:
- return equal || op0lt ? const_true_rtx : const0_rtx;
- case LT:
- return op0lt ? const_true_rtx : const0_rtx;
- case GE:
- return equal || op1lt ? const_true_rtx : const0_rtx;
- case GT:
- return op1lt ? const_true_rtx : const0_rtx;
- }
+ /* Get the two words comprising each integer constant. */
+ if (GET_CODE (op0) == CONST_DOUBLE)
+ {
+ l0u = l0s = CONST_DOUBLE_LOW (op0);
+ h0u = h0s = CONST_DOUBLE_HIGH (op0);
}
-#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
-
- else if (GET_MODE_CLASS (mode) == MODE_INT
- && width > HOST_BITS_PER_WIDE_INT
- && (GET_CODE (op0) == CONST_DOUBLE
- || GET_CODE (op0) == CONST_INT)
- && (GET_CODE (op1) == CONST_DOUBLE
- || GET_CODE (op1) == CONST_INT))
+ else
{
- HOST_WIDE_INT h0, l0, h1, l1;
- unsigned HOST_WIDE_INT uh0, ul0, uh1, ul1;
- int op0lt, op0ltu, equal;
-
- if (GET_CODE (op0) == CONST_DOUBLE)
- l0 = CONST_DOUBLE_LOW (op0), h0 = CONST_DOUBLE_HIGH (op0);
- else
- l0 = INTVAL (op0), h0 = l0 < 0 ? -1 : 0;
+ l0u = l0s = INTVAL (op0);
+ h0u = 0, h0s = l0s < 0 ? -1 : 0;
+ }
- if (GET_CODE (op1) == CONST_DOUBLE)
- l1 = CONST_DOUBLE_LOW (op1), h1 = CONST_DOUBLE_HIGH (op1);
- else
- l1 = INTVAL (op1), h1 = l1 < 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 = 0, h1s = l1s < 0 ? -1 : 0;
+ }
- uh0 = h0, ul0 = l0, uh1 = h1, ul1 = l1;
+ /* If WIDTH is nonzero and smaller than HOST_BITS_PER_WIDE_INT,
+ we have to sign or zero-extend the values. */
+ if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
+ h0u = h1u = 0, h0s = l0s < 0 ? -1 : 0, h1s = l1s < 0 ? -1 : 0;
- equal = (h0 == h1 && l0 == l1);
- op0lt = (h0 < h1 || (h0 == h1 && l0 < l1));
- op0ltu = (uh0 < uh1 || (uh0 == uh1 && ul0 < ul1));
+ if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
+ {
+ l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
+ l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
- switch (code)
- {
- case EQ:
- return equal ? const_true_rtx : const0_rtx;
- case NE:
- return !equal ? const_true_rtx : const0_rtx;
- case LE:
- return equal || op0lt ? const_true_rtx : const0_rtx;
- case LT:
- return op0lt ? const_true_rtx : const0_rtx;
- case GE:
- return !op0lt ? const_true_rtx : const0_rtx;
- case GT:
- return !equal && !op0lt ? const_true_rtx : const0_rtx;
- case LEU:
- return equal || op0ltu ? const_true_rtx : const0_rtx;
- case LTU:
- return op0ltu ? const_true_rtx : const0_rtx;
- case GEU:
- return !op0ltu ? const_true_rtx : const0_rtx;
- case GTU:
- return !equal && !op0ltu ? const_true_rtx : const0_rtx;
- }
+ 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));
+ }
+
+ /* Otherwise, there are some code-specific tests we can make. */
+ else
+ {
switch (code)
{
case EQ:
- {
-#if 0
- /* We can't make this assumption due to #pragma weak */
- if (CONSTANT_P (op0) && op1 == const0_rtx)
- return const0_rtx;
+ /* References to the frame plus a constant or labels cannot
+ be zero, but a SYMBOL_REF can due to #pragma weak. */
+ if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
+ || GET_CODE (op0) == LABEL_REF)
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ /* On some machines, the ap reg can be 0 sometimes. */
+ && op0 != arg_pointer_rtx
#endif
- if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx
- /* On some machines, the ap reg can be 0 sometimes. */
- && op0 != arg_pointer_rtx)
- return const0_rtx;
- break;
- }
+ )
+ return const0_rtx;
+ break;
case NE:
-#if 0
- /* We can't make this assumption due to #pragma weak */
- if (CONSTANT_P (op0) && op1 == const0_rtx)
- return const_true_rtx;
+ 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
- if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx
- /* On some machines, the ap reg can be 0 sometimes. */
- && op0 != arg_pointer_rtx)
+ )
return const_true_rtx;
break;
case GEU:
- /* Unsigned values are never negative, but we must be sure we are
- actually comparing a value, not a CC operand. */
- if (op1 == const0_rtx && INTEGRAL_MODE_P (mode))
+ /* Unsigned values are never negative. */
+ if (op1 == const0_rtx)
return const_true_rtx;
break;
case LTU:
- if (op1 == const0_rtx && INTEGRAL_MODE_P (mode))
+ if (op1 == const0_rtx)
return const0_rtx;
break;
unsigned value. */
if (GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == GET_MODE_MASK (mode)
- && INTEGRAL_MODE_P (mode))
- return const_true_rtx;
+ && INTEGRAL_MODE_P (mode))
+ return const_true_rtx;
break;
case GTU:
return 0;
}
- /* Get the integer argument values in two forms:
- zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */
-
- 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;
- }
-
- /* Compute the value of the arithmetic. */
-
+ /* If we reach here, EQUAL, OP0LT, OP0LTU, OP1LT, and OP1LTU are set
+ as appropriate. */
switch (code)
{
- case NE:
- val = arg0 != arg1 ? STORE_FLAG_VALUE : 0;
- break;
-
case EQ:
- val = arg0 == arg1 ? STORE_FLAG_VALUE : 0;
- break;
-
- case LE:
- val = arg0s <= arg1s ? STORE_FLAG_VALUE : 0;
- break;
-
+ return equal ? const_true_rtx : const0_rtx;
+ case NE:
+ return ! equal ? const_true_rtx : const0_rtx;
case LT:
- val = arg0s < arg1s ? STORE_FLAG_VALUE : 0;
- break;
-
- case GE:
- val = arg0s >= arg1s ? STORE_FLAG_VALUE : 0;
- break;
-
+ return op0lt ? const_true_rtx : const0_rtx;
case GT:
- val = arg0s > arg1s ? STORE_FLAG_VALUE : 0;
- break;
-
- case LEU:
- val = (((unsigned HOST_WIDE_INT) arg0)
- <= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
- break;
-
+ return op1lt ? const_true_rtx : const0_rtx;
case LTU:
- val = (((unsigned HOST_WIDE_INT) arg0)
- < ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
- break;
-
- case GEU:
- val = (((unsigned HOST_WIDE_INT) arg0)
- >= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
- break;
-
+ return op0ltu ? const_true_rtx : const0_rtx;
case GTU:
- val = (((unsigned HOST_WIDE_INT) arg0)
- > ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
- break;
-
- default:
- abort ();
+ 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;
}
- /* Clear the bits that don't belong in our mode, unless they and our sign
- bit are all one. So we get either a reasonable negative value or a
- reasonable unsigned value for this mode. */
- 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);
+ abort ();
}
\f
/* Simplify CODE, an operation with result mode MODE and three operands,
if (op1)
op1 = equiv_constant (op1);
- if (op0 && op1)
+ /* If we are looking for the low SImode part of
+ (ashift:DI c (const_int 32)), it doesn't work
+ to compute that in SImode, because a 32-bit shift
+ in SImode is unpredictable. We know the value is 0. */
+ 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)))
+
+ /* If the count fits in the inner mode's width,
+ but exceeds the outer mode's width,
+ the value will get truncated to 0
+ by the subreg. */
+ new = const0_rtx;
+ else
+ /* If the count exceeds even the inner mode's width,
+ don't fold this expression. */
+ new = 0;
+ }
+ else if (op0 && op1)
new = simplify_binary_operation (GET_CODE (elt->exp), mode,
op0, op1);
}
if (GET_MODE (table) != Pmode)
new = gen_rtx (TRUNCATE, GET_MODE (table), new);
- return new;
+ /* Indicate this is a constant. This isn't a
+ valid form of CONST, but it will only be used
+ to fold the next insns and then discarded, so
+ it should be safe. */
+ return gen_rtx (CONST, GET_MODE (new), new);
}
}
}
switch (GET_RTX_CLASS (code))
{
case '1':
- /* We can't simplify extension ops unless we know the original mode. */
- if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
- && mode_arg0 == VOIDmode)
- break;
- new = simplify_unary_operation (code, mode,
- const_arg0 ? const_arg0 : folded_arg0,
- mode_arg0);
+ {
+ int is_const = 0;
+
+ /* We can't simplify extension ops unless we know the
+ original mode. */
+ if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
+ && mode_arg0 == VOIDmode)
+ break;
+
+ /* If we had a CONST, strip it off and put it back later if we
+ fold. */
+ 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 '<':
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
- true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode);
+ true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+ mode);
false = CONST0_RTX (mode);
}
#endif
since x might be a NaN. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
- || ! FLOAT_MODE_P (mode_arg0))
+ || ! FLOAT_MODE_P (mode_arg0) || flag_fast_math)
&& (folded_arg0 == folded_arg1
|| (GET_CODE (folded_arg0) == REG
&& GET_CODE (folded_arg1) == REG
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
- true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode);
+ true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+ mode);
false = CONST0_RTX (mode);
}
#endif
#ifdef FLOAT_STORE_FLAG_VALUE
if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
new = ((new == const0_rtx) ? CONST0_RTX (mode)
- : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode));
+ : CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE, mode));
#endif
break;
ADDR_DIFF_VEC table. */
if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
{
- rtx y = lookup_as_function (folded_arg0, MINUS);
+ 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);
+
+ /* Now try for a CONST of a MINUS like the above. */
+ 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);
+ }
+
+ /* Likewise if the operands are in the other order. */
+ 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);
+
+ /* Now try for a CONST of a MINUS like the above. */
+ 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 second operand is a register equivalent to a negative
+ CONST_INT, see if we can find a register equivalent to the
+ positive constant. Make a MINUS if so. Don't do this for
+ a negative constant since we might then alternate between
+ chosing positive and negative constants. Having the positive
+ constant previously-used is the more common case. */
+ if (const_arg1 && GET_CODE (const_arg1) == CONST_INT
+ && INTVAL (const_arg1) < 0 && 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;
rtx op0, op1;
int reversed_nonequality;
{
- int op0_hash_code, op1_hash_code;
+ 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;
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
- op0_hash_code = HASH (op0, mode);
+ op0_hash = HASH (op0, mode);
op0_in_memory = hash_arg_in_memory;
op0_in_struct = hash_arg_in_struct;
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
- op1_hash_code = HASH (op1, mode);
+ op1_hash = HASH (op1, mode);
op1_in_memory = hash_arg_in_memory;
op1_in_struct = hash_arg_in_struct;
return;
/* Look up both operands. */
- op0_elt = lookup (op0, op0_hash_code, mode);
- op1_elt = lookup (op1, op1_hash_code, mode);
+ op0_elt = lookup (op0, op0_hash, mode);
+ op1_elt = lookup (op1, op1_hash, mode);
+
+ /* If both operands are already equivalent or if they are not in the
+ table but are identical, do nothing. */
+ 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 we aren't setting two things equal all we can do is save this
comparison. Similarly if this is floating-point. In the latter
if (insert_regs (op0, NULL_PTR, 0))
{
rehash_using_reg (op0);
- op0_hash_code = HASH (op0, mode);
+ op0_hash = HASH (op0, mode);
/* If OP0 is contained in OP1, this changes its hash code
as well. Faster to rehash than to check, except
for the simple case of a constant. */
if (! CONSTANT_P (op1))
- op1_hash_code = HASH (op1,mode);
+ op1_hash = HASH (op1,mode);
}
- op0_elt = insert (op0, NULL_PTR, op0_hash_code, 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 (GET_CODE (op1) == REG)
{
/* Look it up again--in case op0 and op1 are the same. */
- op1_elt = lookup (op1, op1_hash_code, mode);
+ op1_elt = lookup (op1, op1_hash, mode);
/* Put OP1 in the hash table so it gets a new quantity number. */
if (op1_elt == 0)
if (insert_regs (op1, NULL_PTR, 0))
{
rehash_using_reg (op1);
- op1_hash_code = HASH (op1, mode);
+ op1_hash = HASH (op1, mode);
}
- op1_elt = insert (op1, NULL_PTR, op1_hash_code, mode);
+ op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
op1_elt->in_struct = op1_in_struct;
}
return;
}
- /* If both are equivalent, merge the two classes. Save this class for
- `cse_set_around_loop'. */
- if (op0_elt && op1_elt)
- {
- merge_equiv_classes (op0_elt, op1_elt);
- last_jump_equiv_class = op0_elt;
- }
+ /* If either side is still missing an equivalence, make it now,
+ then merge the equivalences. */
- /* For whichever side doesn't have an equivalence, make one. */
if (op0_elt == 0)
{
- if (insert_regs (op0, op1_elt, 0))
+ if (insert_regs (op0, NULL_PTR, 0))
{
rehash_using_reg (op0);
- op0_hash_code = HASH (op0, mode);
+ op0_hash = HASH (op0, mode);
}
- op0_elt = insert (op0, op1_elt, op0_hash_code, mode);
+ op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
op0_elt->in_struct = op0_in_struct;
- last_jump_equiv_class = op0_elt;
}
if (op1_elt == 0)
{
- if (insert_regs (op1, op0_elt, 0))
+ if (insert_regs (op1, NULL_PTR, 0))
{
rehash_using_reg (op1);
- op1_hash_code = HASH (op1, mode);
+ op1_hash = HASH (op1, mode);
}
- op1_elt = insert (op1, op0_elt, op1_hash_code, mode);
+ op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
op1_elt->in_struct = op1_in_struct;
- last_jump_equiv_class = op1_elt;
}
+
+ merge_equiv_classes (op0_elt, op1_elt);
+ last_jump_equiv_class = op0_elt;
}
\f
/* CSE processing for one instruction.
rtx src;
/* The hash-table element for the SET_SRC of the SET. */
struct table_elt *src_elt;
- /* Hash code for the SET_SRC. */
- int src_hash_code;
- /* Hash code for the SET_DEST. */
- int dest_hash_code;
+ /* Hash value for the SET_SRC. */
+ unsigned src_hash;
+ /* Hash value for the SET_DEST. */
+ unsigned dest_hash;
/* The SET_DEST, with SUBREG, etc., stripped. */
rtx inner_dest;
/* Place where the pointer to the INNER_DEST was found. */
enum machine_mode mode;
/* A constant equivalent for SET_SRC, if any. */
rtx src_const;
- /* Hash code of constant equivalent for SET_SRC. */
- int src_const_hash_code;
+ /* Hash value of constant equivalent for SET_SRC. */
+ unsigned src_const_hash;
/* Table entry for constant equivalent for SET_SRC, if any. */
struct table_elt *src_const_elt;
};
int in_libcall_block;
{
register rtx x = PATTERN (insn);
- rtx tem;
register int i;
+ rtx tem;
register int n_sets = 0;
/* Records what this insn does to set CC0. */
int src_eqv_volatile;
int src_eqv_in_memory;
int src_eqv_in_struct;
- int src_eqv_hash_code;
+ unsigned src_eqv_hash;
struct set *sets;
Also determine whether there is a CLOBBER that invalidates
all memory references, or all references at varying addresses. */
+ 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));
canon_reg (SET_SRC (x), insn);
apply_change_group ();
fold_rtx (SET_SRC (x), insn);
- invalidate (SET_DEST (x));
+ invalidate (SET_DEST (x), VOIDmode);
}
else
n_sets = 1;
for (i = 0; i < lim; i++)
{
register rtx y = XVECEXP (x, 0, i);
- if (GET_CODE (y) == CLOBBER
- && (GET_CODE (XEXP (y, 0)) == REG
- || GET_CODE (XEXP (y, 0)) == SUBREG))
- invalidate (XEXP (y, 0));
+ 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++)
canon_reg (SET_SRC (y), insn);
apply_change_group ();
fold_rtx (SET_SRC (y), insn);
- invalidate (SET_DEST (y));
+ invalidate (SET_DEST (y), VOIDmode);
}
else if (SET_DEST (y) == pc_rtx
&& GET_CODE (SET_SRC (y)) == LABEL_REF)
fold_rtx (x, insn);
}
- if (n_sets == 1 && REG_NOTES (insn) != 0)
- {
- /* Store the equivalent value in SRC_EQV, if different. */
- rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
-
- if (tem && ! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)))
- src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX);
- }
+ /* Store the equivalent value in SRC_EQV, if different, or if the DEST
+ is a STRICT_LOW_PART. The latter condition is necessary because SRC_EQV
+ is handled specially for this case, and if it isn't set, then there will
+ be no equivalence for the destinatation. */
+ 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);
/* Canonicalize sources and addresses of destinations.
We do this in a separate pass to avoid problems when a MATCH_DUP is
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
src_eqv = fold_rtx (src_eqv, insn);
- src_eqv_hash_code = HASH (src_eqv, eqvmode);
+ src_eqv_hash = HASH (src_eqv, eqvmode);
/* Find the equivalence class for the equivalent expression. */
if (!do_not_record)
- src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, eqvmode);
+ src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode);
src_eqv_volatile = do_not_record;
src_eqv_in_memory = hash_arg_in_memory;
hash_arg_in_struct = 0;
sets[i].src = src;
- sets[i].src_hash_code = HASH (src, mode);
+ 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;
REG_NOTE. */
if (!sets[i].src_volatile)
- elt = lookup (src, sets[i].src_hash_code, mode);
+ elt = lookup (src, sets[i].src_hash, mode);
sets[i].src_elt = elt;
/* The REG_EQUAL is indicating that two formerly distinct
classes are now equivalent. So merge them. */
merge_equiv_classes (elt, src_eqv_elt);
- src_eqv_hash_code = HASH (src_eqv, elt->mode);
- src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, elt->mode);
+ src_eqv_hash = HASH (src_eqv, elt->mode);
+ src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode);
}
src_eqv_here = 0;
/* Try to find a constant somewhere and record it in `src_const'.
Record its table element, if any, in `src_const_elt'. Look in
any known equivalences first. (If the constant is not in the
- table, also set `sets[i].src_const_hash_code'). */
+ table, also set `sets[i].src_const_hash'). */
if (elt)
for (p = elt->first_same_value; p; p = p->next_same_value)
if (p->is_const)
hash code and look it up. */
if (src_const && src_const_elt == 0)
{
- sets[i].src_const_hash_code = HASH (src_const, mode);
- src_const_elt = lookup (src_const, sets[i].src_const_hash_code,
- mode);
+ 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;
}
}
}
-
+
+#ifdef LOAD_EXTEND_OP
+ /* See if a MEM has already been loaded with a widening operation;
+ if it has, we can use a subreg of that. Many CISC machines
+ also have such operations, but this is only likely to be
+ beneficial these machines. */
+
+ 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;
+
+ /* Set what we are trying to extend and the operation it might
+ have been extended with. */
+ 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 /* LOAD_EXTEND_OP */
+
if (src == src_folded)
src_folded = 0;
trial = src, src_cost = 10000;
else if (src_eqv_cost <= src_related_cost
&& src_eqv_cost <= src_elt_cost)
- trial = src_eqv_here, src_eqv_cost = 10000;
+ trial = copy_rtx (src_eqv_here), src_eqv_cost = 10000;
else if (src_related_cost <= src_elt_cost)
- trial = src_related, src_related_cost = 10000;
+ trial = copy_rtx (src_related), src_related_cost = 10000;
else
{
trial = copy_rtx (elt->exp);
trial = gen_rtx (LABEL_REF, Pmode, get_label_after (trial));
SET_SRC (sets[i].rtl) = trial;
+ cse_jumps_altered = 1;
break;
}
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
sets[i].src = src;
- sets[i].src_hash_code = HASH (src, mode);
+ 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_code, mode);
+ sets[i].src_elt = lookup (src, sets[i].src_hash, mode);
}
/* If this is a single SET, we are setting a register, and we have an
if (n_sets == 1 && src_const && GET_CODE (dest) == REG
&& GET_CODE (src_const) != REG)
{
- rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+ tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
/* Record the actual constant value in a REG_EQUAL note, making
a new one if one does not already exist. */
before the effects of this instruction are recorded,
since the register values used in the address computation
are those before this instruction. */
- sets[i].dest_hash_code = HASH (dest, mode);
+ sets[i].dest_hash = HASH (dest, mode);
/* Don't enter a bit-field in the hash table
because the value in it after the store
{
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == MEM)
- invalidate (dest);
+ invalidate (dest, VOIDmode);
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ invalidate (XEXP (dest, 0), GET_MODE (dest));
sets[i].rtl = 0;
}
if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
- sets[i].dest_hash_code = HASH (SET_DEST (sets[i].rtl), mode);
+ sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
#ifdef HAVE_cc0
/* If setting CC0, record what it was set to, or a constant, if it
classp = 0;
}
if (insert_regs (src_eqv, classp, 0))
- src_eqv_hash_code = HASH (src_eqv, eqvmode);
- elt = insert (src_eqv, classp, src_eqv_hash_code, eqvmode);
+ 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;
This is a more interesting equivalence, so we arrange later
to treat the entire reg as the destination. */
sets[i].src_elt = src_eqv_elt;
- sets[i].src_hash_code = src_eqv_hash_code;
+ sets[i].src_hash = src_eqv_hash;
}
else
{
any of the src_elt's, because they would have failed to
match if not still valid. */
if (insert_regs (src, classp, 0))
- sets[i].src_hash_code = HASH (src, mode);
- elt = insert (src, classp, sets[i].src_hash_code, mode);
+ 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;
&& 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_code, mode);
+ sets[i].src_const_hash, mode);
}
}
else if (sets[i].src_elt == 0)
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
- register rtx dest = sets[i].inner_dest;
+ /* We can't use the inner dest, because the mode associated with
+ a ZERO_EXTRACT is significant. */
+ register rtx dest = SET_DEST (sets[i].rtl);
/* Needed for registers to remove the register from its
previous quantity's chain.
Needed for memory if this is a nonvarying address, unless
we have just done an invalidate_memory that covers even those. */
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
- || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
- invalidate (dest);
+ || (GET_CODE (dest) == MEM && ! writes_memory.all
+ && ! cse_rtx_addr_varies_p (dest)))
+ invalidate (dest, VOIDmode);
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ invalidate (XEXP (dest, 0), GET_MODE (dest));
}
/* Make sure registers mentioned in destinations
if (insert_regs (dest, sets[i].src_elt, 1))
/* If `insert_regs' changes something, the hash code must be
recalculated. */
- sets[i].dest_hash_code = HASH (dest, GET_MODE (dest));
+ sets[i].dest_hash = HASH (dest, GET_MODE (dest));
elt = insert (dest, sets[i].src_elt,
- sets[i].dest_hash_code, GET_MODE (dest));
+ sets[i].dest_hash, GET_MODE (dest));
elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM;
if (elt->in_memory)
{
already entered SRC and DEST of the SET in the table. */
if (GET_CODE (dest) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) / UNITS_PER_WORD
- == GET_MODE_SIZE (GET_MODE (dest)) / UNITS_PER_WORD)
+ && (((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)
elt = elt->next_same_value)
{
rtx new_src = 0;
- int src_hash;
+ unsigned src_hash;
struct table_elt *src_elt;
/* Ignore invalid entries. */
}
else if (GET_MODE (written) == BLKmode)
*writes_ptr = everything;
+ /* (mem (scratch)) means clobber everything. */
+ else if (GET_CODE (addr) == SCRATCH)
+ *writes_ptr = everything;
else if (cse_rtx_addr_varies_p (written))
{
/* A varying address that is a sum indicates an array element,
/* This should be *very* rare. */
if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
- invalidate (stack_pointer_rtx);
+ invalidate (stack_pointer_rtx, VOIDmode);
}
if (GET_CODE (x) == CLOBBER)
{
rtx ref = XEXP (x, 0);
- if (ref
- && (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
- || (GET_CODE (ref) == MEM && ! w->all)))
- invalidate (ref);
+ if (ref)
+ {
+ if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
+ || (GET_CODE (ref) == MEM && ! w->all))
+ 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)
{
if (GET_CODE (y) == CLOBBER)
{
rtx ref = XEXP (y, 0);
- if (ref
- &&(GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
- || (GET_CODE (ref) == MEM && !w->all)))
- invalidate (ref);
+ if (ref)
+ {
+ if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
+ || (GET_CODE (ref) == MEM && !w->all))
+ invalidate (ref, VOIDmode);
+ else if (GET_CODE (ref) == STRICT_LOW_PART
+ || GET_CODE (ref) == ZERO_EXTRACT)
+ invalidate (XEXP (ref, 0), GET_MODE (ref));
+ }
}
}
}
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)
- invalidate (p->exp);
+ || (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));
/* Process insns starting after LOOP_START until we hit a CALL_INSN or
a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it).
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|| (! skipped_writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
- invalidate (dest);
+ invalidate (dest, VOIDmode);
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ invalidate (XEXP (dest, 0), GET_MODE (dest));
}
/* Invalidate all insns from START up to the end of the function or the
if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
|| (GET_CODE (SET_DEST (x)) == MEM && ! writes_memory.all
&& ! cse_rtx_addr_varies_p (SET_DEST (x))))
- invalidate (SET_DEST (x));
+ 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)));
}
\f
/* Find the end of INSN's basic block and return its range,
reg_in_table = (int *) alloca (nregs * sizeof (int));
reg_tick = (int *) alloca (nregs * sizeof (int));
+#ifdef LOAD_EXTEND_OP
+
+ /* Allocate scratch rtl here. cse_insn will fill in the memory reference
+ and change the code and mode as appropriate. */
+ memory_extend_rtx = gen_rtx (ZERO_EXTEND, VOIDmode, 0);
+#endif
+
/* Discard all the free elements of the previous function
since they are allocated in the temporarily obstack. */
- bzero (table, sizeof table);
+ 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 (uid_cuid, (max_uid + 1) * sizeof (int));
+ bzero ((char *) uid_cuid, (max_uid + 1) * sizeof (int));
/* Compute the mapping from uids to cuids.
CUIDs are numbers assigned to insns, like uids,
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
#endif
-#ifdef PIC_OFFSET_TABLE_REGNUM
+#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
&& ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
#endif
)
\f
/* Count the number of times registers are used (not set) in X.
COUNTS is an array in which we accumulate the count, INCR is how much
- we count each register usage. */
+ we count each register usage.
+
+ Don't count a usage of DEST, which is the SET_DEST of a SET which
+ contains X in its SET_SRC. This is because such a SET does not
+ modify the liveness of DEST. */
static void
-count_reg_usage (x, counts, incr)
+count_reg_usage (x, counts, dest, incr)
rtx x;
int *counts;
+ rtx dest;
int incr;
{
- enum rtx_code code = GET_CODE (x);
+ enum rtx_code code;
char *fmt;
int i, j;
- switch (code)
+ if (x == 0)
+ return;
+
+ switch (code = GET_CODE (x))
{
case REG:
- counts[REGNO (x)] += incr;
+ if (x != dest)
+ counts[REGNO (x)] += incr;
return;
case PC:
case SET:
/* Unless we are setting a REG, count everything in SET_DEST. */
if (GET_CODE (SET_DEST (x)) != REG)
- count_reg_usage (SET_DEST (x), counts, incr);
- count_reg_usage (SET_SRC (x), counts, incr);
+ count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
+
+ /* If SRC has side-effects, then we can't delete this insn, so the
+ usage of SET_DEST inside SRC counts.
+
+ ??? Strictly-speaking, we might be preserving this insn
+ because some other SET has side-effects, but that's hard
+ to do and can't happen now. */
+ 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);
+
+ /* ... falls through ... */
case INSN:
case JUMP_INSN:
- case CALL_INSN:
- count_reg_usage (PATTERN (x), counts, incr);
+ count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
/* Things used in a REG_EQUAL note aren't dead since loop may try to
use them. */
- if (REG_NOTES (x))
- count_reg_usage (REG_NOTES (x), counts, 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)
- count_reg_usage (XEXP (x, 0), counts, incr);
- if (XEXP (x, 1))
- count_reg_usage (XEXP (x, 1), counts, incr);
+ if (REG_NOTE_KIND (x) == REG_EQUAL
+ || 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;
}
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- count_reg_usage (XEXP (x, i), counts, incr);
+ 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, incr);
+ count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
}
}
\f
int in_libcall = 0;
/* First count the number of times each register is used. */
- bzero (counts, sizeof (int) * nreg);
+ bzero ((char *) counts, sizeof (int) * nreg);
for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
- count_reg_usage (insn, counts, 1);
+ count_reg_usage (insn, counts, NULL_RTX, 1);
/* Go from the last insn to the first and delete insns that only set unused
registers or copy a register to itself. As we delete an insn, remove
if (! live_insn)
{
- count_reg_usage (insn, counts, -1);
+ count_reg_usage (insn, counts, NULL_RTX, -1);
delete_insn (insn);
}