static void inc_for_reload PROTO((rtx, rtx, int));
static int constraint_accepts_reg_p PROTO((char *, rtx));
static int count_occurrences PROTO((rtx, rtx));
+static void reload_cse_invalidate_regno PROTO((int, enum machine_mode, int));
+static int reload_cse_mem_conflict_p PROTO((rtx, rtx, enum machine_mode,
+ rtx));
+static void reload_cse_invalidate_mem PROTO((rtx));
+static void reload_cse_invalidate_rtx PROTO((rtx, rtx));
+static void reload_cse_regs PROTO((rtx));
+static int reload_cse_regno_equal_p PROTO((int, rtx, enum machine_mode));
+static int reload_cse_noop_set_p PROTO((rtx, rtx));
+static void reload_cse_simplify_set PROTO((rtx, rtx));
+static void reload_cse_check_clobber PROTO((rtx, rtx));
+static void reload_cse_record_set PROTO((rtx, rtx));
\f
/* Initialize the reload pass once per compilation. */
register rtx insn;
register struct elim_table *ep;
+ /* The two pointers used to track the true location of the memory used
+ for label offsets. */
+ char *real_known_ptr = NULL_PTR;
+ int (*real_at_ptr)[NUM_ELIMINABLE_REGS];
+
int something_changed;
int something_needs_reloads;
int something_needs_elimination;
as homes for pseudo registers.
This is done here rather than (eg) in global_alloc
because this point is reached even if not optimizing. */
-
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
mark_home_live (i);
+ /* A function that receives a nonlocal goto must save all call-saved
+ registers. */
+ if (current_function_has_nonlocal_label)
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (! call_used_regs[i] && ! fixed_regs[i])
+ regs_ever_live[i] = 1;
+ }
+
for (i = 0; i < scratch_list_length; i++)
if (scratch_list[i])
mark_scratch_live (scratch_list[i]);
num_labels = max_label_num () - get_first_label_num ();
/* Allocate the tables used to store offset information at labels. */
- offsets_known_at = (char *) alloca (num_labels);
- offsets_at
+ /* We used to use alloca here, but the size of what it would try to
+ allocate would occasionally cause it to exceed the stack limit and
+ cause a core dump. */
+ real_known_ptr = xmalloc (num_labels);
+ real_at_ptr
= (int (*)[NUM_ELIMINABLE_REGS])
- alloca (num_labels * NUM_ELIMINABLE_REGS * sizeof (int));
+ xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (int));
- offsets_known_at -= get_first_label_num ();
- offsets_at -= get_first_label_num ();
+ offsets_known_at = real_known_ptr - get_first_label_num ();
+ offsets_at
+ = (int (*)[NUM_ELIMINABLE_REGS]) (real_at_ptr - get_first_label_num ());
/* Alter each pseudo-reg rtx to contain its hard reg number.
Assign stack slots to the pseudos that lack hard regs or equivalents.
for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
alter_reg (i, -1);
- /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done here
- because the stack size may be a part of the offset computation for
- register elimination. */
- assign_stack_local (BLKmode, 0, 0);
-
/* If we have some registers we think can be eliminated, scan all insns to
see if there is an insn that sets one of these registers to something
other than itself plus a constant. If so, the register cannot be
in that case some pseudos might be in the wrong kind of hard reg. */
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
- if (reg_renumber[i] == -1 && reg_n_refs[i] != 0)
+ if (reg_renumber[i] == -1 && REG_N_REFS (i) != 0)
break;
if (i == max_regno && num_eliminable == 0 && ! caller_save_needed)
- return;
+ {
+ free (real_known_ptr);
+ free (real_at_ptr);
+ return;
+ }
#endif
/* Compute the order of preference for hard registers to spill.
rtx max_groups_insn[N_REG_CLASSES];
rtx max_nongroups_insn[N_REG_CLASSES];
rtx x;
- HOST_WIDE_INT starting_frame_size = get_frame_size ();
+ HOST_WIDE_INT starting_frame_size;
int previous_frame_pointer_needed = frame_pointer_needed;
static char *reg_class_names[] = REG_CLASS_NAMES;
changes from 0 to 1 in this pass. */
new_basic_block_needs = 0;
+ /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done
+ here because the stack size may be a part of the offset computation
+ for register elimination, and there might have been new stack slots
+ created in the last iteration of this loop. */
+ assign_stack_local (BLKmode, 0, 0);
+
+ starting_frame_size = get_frame_size ();
+
/* Reset all offsets on eliminable registers to their initial values. */
#ifdef ELIMINABLE_REGS
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
if (reg_renumber[i] < 0 && reg_equiv_memory_loc[i])
{
- rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX);
+ rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX, 0);
if (strict_memory_address_p (GET_MODE (regno_reg_rtx[i]),
XEXP (x, 0)))
struct needs op_addr;
struct needs op_addr_reload;
struct needs in_addr[MAX_RECOG_OPERANDS];
+ struct needs in_addr_addr[MAX_RECOG_OPERANDS];
struct needs out_addr[MAX_RECOG_OPERANDS];
+ struct needs out_addr_addr[MAX_RECOG_OPERANDS];
} insn_needs;
/* If needed, eliminate any eliminable registers. */
case RELOAD_FOR_INPUT_ADDRESS:
this_needs = &insn_needs.in_addr[reload_opnum[i]];
break;
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ this_needs = &insn_needs.in_addr_addr[reload_opnum[i]];
+ break;
case RELOAD_FOR_OUTPUT_ADDRESS:
this_needs = &insn_needs.out_addr[reload_opnum[i]];
break;
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ this_needs = &insn_needs.out_addr_addr[reload_opnum[i]];
+ break;
case RELOAD_FOR_OPERAND_ADDRESS:
this_needs = &insn_needs.op_addr;
break;
{
in_max
= MAX (in_max, insn_needs.in_addr[k].regs[j][i]);
+ in_max
+ = MAX (in_max,
+ insn_needs.in_addr_addr[k].regs[j][i]);
out_max
= MAX (out_max, insn_needs.out_addr[k].regs[j][i]);
+ out_max
+ = MAX (out_max,
+ insn_needs.out_addr_addr[k].regs[j][i]);
}
/* RELOAD_FOR_INSN reloads conflict with inputs, outputs,
j < reload_n_operands; j++)
{
in_max = MAX (in_max, insn_needs.in_addr[j].groups[i]);
+ in_max = MAX (in_max,
+ insn_needs.in_addr_addr[j].groups[i]);
out_max
= MAX (out_max, insn_needs.out_addr[j].groups[i]);
+ out_max
+ = MAX (out_max, insn_needs.out_addr_addr[j].groups[i]);
}
in_max = MAX (MAX (insn_needs.op_addr.groups[i],
}
}
+ /* Do a very simple CSE pass over just the hard registers. */
+ if (optimize > 0)
+ reload_cse_regs (first);
+
#ifdef PRESERVE_DEATH_INFO_REGNO_P
/* Make a pass over all the insns and remove death notes for things that
are no longer registers or no longer die in the insn (e.g., an input
reg_equiv_constant = 0;
reg_equiv_memory_loc = 0;
+ if (real_known_ptr)
+ free (real_known_ptr);
+ if (real_at_ptr)
+ free (real_at_ptr);
+
if (scratch_list)
free (scratch_list);
scratch_list = 0;
allocate a stack slot for it. */
if (reg_renumber[i] < 0
- && reg_n_refs[i] > 0
+ && REG_N_REFS (i) > 0
&& reg_equiv_constant[i] == 0
&& reg_equiv_memory_loc[i] == 0)
{
if (from_reg == -1)
{
/* No known place to spill from => no slot to reuse. */
- x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size, -1);
+ x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size,
+ inherent_size == total_size ? 0 : -1);
if (BYTES_BIG_ENDIAN)
/* Cancel the big-endian correction done in assign_stack_local.
Get the address of the beginning of the slot.
total_size = spill_stack_slot_width[from_reg];
}
/* Make a slot with that size. */
- x = assign_stack_local (mode, total_size, -1);
+ x = assign_stack_local (mode, total_size,
+ inherent_size == total_size ? 0 : -1);
stack_slot = x;
if (BYTES_BIG_ENDIAN)
{
the proper thing. */
rtx
-eliminate_regs (x, mem_mode, insn)
+eliminate_regs (x, mem_mode, insn, storing)
rtx x;
enum machine_mode mem_mode;
rtx insn;
+ int storing;
{
enum rtx_code code = GET_CODE (x);
struct elim_table *ep;
reference to the pseudo. Ensure we make a copy of the
address in case it is shared. */
new = eliminate_regs (reg_equiv_memory_loc[regno],
- mem_mode, insn);
+ mem_mode, insn, 0);
if (new != reg_equiv_memory_loc[regno])
{
cannot_omit_stores[regno] = 1;
context.
If we have (plus (eliminable) (reg)), we want to produce
- (plus (plus (replacement) (reg)) (const)). If this was part of a
+ (plus (plus (replacement) (reg) (const))). If this was part of a
normal add insn, (plus (replacement) (reg)) will be pushed as a
reload. This is the desired action. */
{
- rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
- rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+ rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
+ rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn, 0);
if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
{
}
return x;
- case MINUS:
- /* If we have (minus (eliminable) (reg)), we want to produce
- (plus (minus (replacement) (reg)) (const)). The main reason being
- to be consistent with what is done for PLUS. find_reloads_address
- assumes that we do this. */
- {
- rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
- rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn);
-
- if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
- {
- if (GET_CODE (new0) == PLUS)
- return gen_rtx (PLUS, GET_MODE (x),
- gen_rtx (MINUS, GET_MODE (x),
- XEXP (new0, 0), new1),
- XEXP (new0, 1));
- else
- return gen_rtx (MINUS, GET_MODE (x), new0, new1);
- }
- }
- return x;
-
case MULT:
/* If this is the product of an eliminable register and a
constant, apply the distribute law and move the constant out
case CALL:
case COMPARE:
+ case MINUS:
case DIV: case UDIV:
case MOD: case UMOD:
case AND: case IOR: case XOR:
case GE: case GT: case GEU: case GTU:
case LE: case LT: case LEU: case LTU:
{
- rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
rtx new1
- = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn) : 0;
+ = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn, 0) : 0;
if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
return gen_rtx (code, GET_MODE (x), new0, new1);
/* If we have something in XEXP (x, 0), the usual case, eliminate it. */
if (XEXP (x, 0))
{
- new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
if (new != XEXP (x, 0))
x = gen_rtx (EXPR_LIST, REG_NOTE_KIND (x), new, XEXP (x, 1));
}
strictly needed, but it simplifies the code. */
if (XEXP (x, 1))
{
- new = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+ new = eliminate_regs (XEXP (x, 1), mem_mode, insn, 0);
if (new != XEXP (x, 1))
return gen_rtx (GET_CODE (x), GET_MODE (x), XEXP (x, 0), new);
}
case ABS:
case SQRT:
case FFS:
- new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
if (new != XEXP (x, 0))
return gen_rtx (code, GET_MODE (x), new);
return x;
&& reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0)
{
new = eliminate_regs (reg_equiv_memory_loc[REGNO (SUBREG_REG (x))],
- mem_mode, insn);
+ mem_mode, insn, 0);
/* If we didn't change anything, we must retain the pseudo. */
if (new == reg_equiv_memory_loc[REGNO (SUBREG_REG (x))])
}
}
else
- new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
+ new = eliminate_regs (SUBREG_REG (x), mem_mode, insn, 0);
if (new != XEXP (x, 0))
{
- if (GET_CODE (new) == MEM
- && (GET_MODE_SIZE (GET_MODE (x))
- <= GET_MODE_SIZE (GET_MODE (new)))
+ int x_size = GET_MODE_SIZE (GET_MODE (x));
+ int new_size = GET_MODE_SIZE (GET_MODE (new));
+
+ /* When asked to spill a partial word subreg, we need to go
+ ahead and spill the whole thing against the possibility
+ that we reload the whole reg and find garbage at the top. */
+ if (storing
+ && GET_CODE (new) == MEM
+ && x_size < new_size
+ && ((x_size + UNITS_PER_WORD-1) / UNITS_PER_WORD
+ == (new_size + UNITS_PER_WORD-1) / UNITS_PER_WORD))
+ return new;
+ else if (GET_CODE (new) == MEM
+ && x_size <= new_size
#ifdef LOAD_EXTEND_OP
- /* On these machines we will be reloading what is
- inside the SUBREG if it originally was a pseudo and
- the inner and outer modes are both a word or
- smaller. So leave the SUBREG then. */
- && ! (GET_CODE (SUBREG_REG (x)) == REG
- && GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD
- && GET_MODE_SIZE (GET_MODE (new)) <= UNITS_PER_WORD
- && (GET_MODE_SIZE (GET_MODE (x))
- > GET_MODE_SIZE (GET_MODE (new)))
- && INTEGRAL_MODE_P (GET_MODE (new))
- && LOAD_EXTEND_OP (GET_MODE (new)) != NIL)
+ /* On these machines we will be reloading what is
+ inside the SUBREG if it originally was a pseudo and
+ the inner and outer modes are both a word or
+ smaller. So leave the SUBREG then. */
+ && ! (GET_CODE (SUBREG_REG (x)) == REG
+ && x_size <= UNITS_PER_WORD
+ && new_size <= UNITS_PER_WORD
+ && x_size > new_size
+ && INTEGRAL_MODE_P (GET_MODE (new))
+ && LOAD_EXTEND_OP (GET_MODE (new)) != NIL)
#endif
- )
+ )
{
int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
enum machine_mode mode = GET_MODE (x);
if (ep->from_rtx == XEXP (x, 0))
ep->can_eliminate = 0;
- new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
if (new != XEXP (x, 0))
return gen_rtx (code, GET_MODE (x), new);
return x;
if (ep->to_rtx == XEXP (x, 0))
ep->can_eliminate = 0;
- new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
if (new != XEXP (x, 0))
return gen_rtx (code, GET_MODE (x), new);
return x;
temp_vec = (rtx *) alloca (XVECLEN (x, 3) * sizeof (rtx));
for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
temp_vec[i] = eliminate_regs (ASM_OPERANDS_INPUT (x, i),
- mem_mode, insn);
+ mem_mode, insn, 0);
for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
if (temp_vec[i] != ASM_OPERANDS_INPUT (x, i))
/* Now avoid the loop below in this common case. */
{
- rtx new0 = eliminate_regs (SET_DEST (x), 0, insn);
- rtx new1 = eliminate_regs (SET_SRC (x), 0, insn);
+ rtx new0 = eliminate_regs (SET_DEST (x), 0, insn, 1);
+ rtx new1 = eliminate_regs (SET_SRC (x), 0, insn, 0);
/* If SET_DEST changed from a REG to a MEM and INSN is an insn,
write a CLOBBER insn. */
/* Our only special processing is to pass the mode of the MEM to our
recursive call and copy the flags. While we are here, handle this
case more efficiently. */
- new = eliminate_regs (XEXP (x, 0), GET_MODE (x), insn);
+ new = eliminate_regs (XEXP (x, 0), GET_MODE (x), insn, 0);
if (new != XEXP (x, 0))
{
new = gen_rtx (MEM, GET_MODE (x), new);
{
if (*fmt == 'e')
{
- new = eliminate_regs (XEXP (x, i), mem_mode, insn);
+ new = eliminate_regs (XEXP (x, i), mem_mode, insn, 0);
if (new != XEXP (x, i) && ! copied)
{
rtx new_x = rtx_alloc (code);
int copied_vec = 0;
for (j = 0; j < XVECLEN (x, i); j++)
{
- new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn);
+ new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn, 0);
if (new != XVECEXP (x, i, j) && ! copied_vec)
{
rtvec new_v = gen_rtvec_vv (XVECLEN (x, i),
but now can do this as a load-address. This saves an insn in this
common case. */
- new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX);
+ new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX, 0);
if (new_body != old_body)
{
/* If we aren't replacing things permanently and we changed something,
of spill registers to be needed in the final reload pass than in
the pre-passes. */
if (val && REG_NOTES (insn) != 0)
- REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn));
+ REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn), 0);
if (! replace)
pop_obstacks ();
if (! cant_eliminate
&& basic_block_needs[0]
- && reg_basic_block[i] >= 0
- && basic_block_needs[(int) class][reg_basic_block[i]] == 0)
+ && REG_BASIC_BLOCK (i) >= 0
+ && basic_block_needs[(int) class][REG_BASIC_BLOCK (i)] == 0)
{
enum reg_class *p;
for (p = reg_class_superclasses[(int) class];
*p != LIM_REG_CLASSES; p++)
- if (basic_block_needs[(int) *p][reg_basic_block[i]] > 0)
+ if (basic_block_needs[(int) *p][REG_BASIC_BLOCK (i)] > 0)
break;
if (*p == LIM_REG_CLASSES)
we're not going to be able to reallocate it, but
we might if allocated by global alloc. */
if (global && reg_allocno[i] < 0)
- hard_reg_n_uses[regno].uses += (reg_n_refs[i] + 1) / 2;
+ hard_reg_n_uses[regno].uses += (REG_N_REFS (i) + 1) / 2;
- hard_reg_n_uses[regno++].uses += reg_n_refs[i];
+ hard_reg_n_uses[regno++].uses += REG_N_REFS (i);
}
}
- large += reg_n_refs[i];
+ large += REG_N_REFS (i);
}
/* Now fixed registers (which cannot safely be used for reloading)
&& GET_CODE (XEXP (PATTERN (insn), 0)) == MEM)
XEXP (XEXP (PATTERN (insn), 0), 0)
= eliminate_regs (XEXP (XEXP (PATTERN (insn), 0), 0),
- GET_MODE (XEXP (PATTERN (insn), 0)), NULL_RTX);
+ GET_MODE (XEXP (PATTERN (insn), 0)),
+ NULL_RTX, 0);
/* If we need to do register elimination processing, do so.
This might delete the insn, in which case we are done. */
static HARD_REG_SET reload_reg_used;
/* If reg is in use for a RELOAD_FOR_INPUT_ADDRESS reload for operand I. */
static HARD_REG_SET reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS];
+/* If reg is in use for a RELOAD_FOR_INPADDR_ADDRESS reload for operand I. */
+static HARD_REG_SET reload_reg_used_in_inpaddr_addr[MAX_RECOG_OPERANDS];
/* If reg is in use for a RELOAD_FOR_OUTPUT_ADDRESS reload for operand I. */
static HARD_REG_SET reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS];
+/* If reg is in use for a RELOAD_FOR_OUTADDR_ADDRESS reload for operand I. */
+static HARD_REG_SET reload_reg_used_in_outaddr_addr[MAX_RECOG_OPERANDS];
/* If reg is in use for a RELOAD_FOR_INPUT reload for operand I. */
static HARD_REG_SET reload_reg_used_in_input[MAX_RECOG_OPERANDS];
/* If reg is in use for a RELOAD_FOR_OUTPUT reload for operand I. */
SET_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i);
break;
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ SET_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], i);
+ break;
+
case RELOAD_FOR_OUTPUT_ADDRESS:
SET_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i);
break;
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ SET_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], i);
+ break;
+
case RELOAD_FOR_OPERAND_ADDRESS:
SET_HARD_REG_BIT (reload_reg_used_in_op_addr, i);
break;
CLEAR_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i);
break;
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], i);
+ break;
+
case RELOAD_FOR_OUTPUT_ADDRESS:
CLEAR_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i);
break;
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], i);
+ break;
+
case RELOAD_FOR_OPERAND_ADDRESS:
CLEAR_HARD_REG_BIT (reload_reg_used_in_op_addr, i);
break;
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
return 0;
/* If it is used in a later operand's address, can't use it. */
for (i = opnum + 1; i < reload_n_operands; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno))
return 0;
return 1;
case RELOAD_FOR_INPUT_ADDRESS:
/* Can't use a register if it is used for an input address for this
operand or used as an input in an earlier one. */
- if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], regno))
+ return 0;
+
+ for (i = 0; i < opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ /* Can't use a register if it is used for an input address
+ address for this operand or used as an input in an earlier
+ one. */
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], regno))
return 0;
for (i = 0; i < opnum; i++)
return 1;
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ /* Can't use a register if it is used for an output address
+ address for this operand or used as an output in this or a
+ later operand. */
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], regno))
+ return 0;
+
+ for (i = opnum; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ return 1;
+
case RELOAD_FOR_OPERAND_ADDRESS:
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
return 0;
for (i = 0; i <= opnum; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno))
return 0;
return 1;
the first place, since we know that it was allocated. */
case RELOAD_FOR_OUTPUT_ADDRESS:
+ case RELOAD_FOR_OUTADDR_ADDRESS:
/* Earlier reloads are for earlier outputs or their addresses,
any RELOAD_FOR_INSN reloads, any inputs or their addresses, or any
RELOAD_FOR_OTHER_ADDRESS reloads (we know it can't conflict with
RELOAD_OTHER).. */
for (i = 0; i < opnum; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
return 0;
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
return 0;
anything that can't be used for it, except that we've already
tested for RELOAD_FOR_INSN objects. */
- if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], regno))
return 0;
for (i = 0; i < opnum; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
return 0;
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno))
return 0;
return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
case RELOAD_FOR_OPERAND_ADDRESS:
+ /* Earlier reloads include RELOAD_FOR_OPADDR_ADDR reloads. */
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, regno))
+ return 0;
+
+ /* ... fall through ... */
+
case RELOAD_FOR_OPADDR_ADDR:
case RELOAD_FOR_INSN:
/* These can't conflict with inputs, or each other, so all we have to
test is input addresses and the addresses of OTHER items. */
for (i = 0; i < reload_n_operands; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno))
return 0;
return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
with), and addresses of RELOAD_OTHER objects. */
for (i = 0; i <= opnum; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno))
return 0;
return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
case RELOAD_FOR_INPUT_ADDRESS:
+ case RELOAD_FOR_INPADDR_ADDRESS:
/* Similarly, all we have to check is for use in earlier inputs'
addresses. */
for (i = 0; i < opnum; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno))
return 0;
return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
return 0;
&& ! TEST_HARD_REG_BIT (reload_reg_used, regno));
case RELOAD_FOR_INPUT_ADDRESS:
+ case RELOAD_FOR_INPADDR_ADDRESS:
/* Similar, except that we check only for this and subsequent inputs
and the address of only subsequent inputs and we do not need
to check for RELOAD_OTHER objects since they are known not to
return 0;
for (i = opnum + 1; i < reload_n_operands; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno))
return 0;
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
return 0;
for (i = opnum + 1; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
return 0;
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
return 0;
case RELOAD_FOR_OPADDR_ADDR:
for (i = 0; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
return 0;
case RELOAD_FOR_OUTPUT:
case RELOAD_FOR_OUTPUT_ADDRESS:
+ case RELOAD_FOR_OUTADDR_ADDRESS:
/* We already know these can't conflict with a later output. So the
only thing to check are later output addresses. */
for (i = opnum + 1; i < reload_n_operands; i++)
- if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno))
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno))
return 0;
return 1;
|| r2_type == RELOAD_FOR_OPERAND_ADDRESS
|| r2_type == RELOAD_FOR_OPADDR_ADDR
|| r2_type == RELOAD_FOR_INPUT
- || (r2_type == RELOAD_FOR_INPUT_ADDRESS && r2_opnum > r1_opnum));
+ || ((r2_type == RELOAD_FOR_INPUT_ADDRESS
+ || r2_type == RELOAD_FOR_INPADDR_ADDRESS)
+ && r2_opnum > r1_opnum));
case RELOAD_FOR_INPUT_ADDRESS:
return ((r2_type == RELOAD_FOR_INPUT_ADDRESS && r1_opnum == r2_opnum)
|| (r2_type == RELOAD_FOR_INPUT && r2_opnum < r1_opnum));
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ return ((r2_type == RELOAD_FOR_INPADDR_ADDRESS && r1_opnum == r2_opnum)
+ || (r2_type == RELOAD_FOR_INPUT && r2_opnum < r1_opnum));
+
case RELOAD_FOR_OUTPUT_ADDRESS:
return ((r2_type == RELOAD_FOR_OUTPUT_ADDRESS && r2_opnum == r1_opnum)
|| (r2_type == RELOAD_FOR_OUTPUT && r2_opnum >= r1_opnum));
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ return ((r2_type == RELOAD_FOR_OUTADDR_ADDRESS && r2_opnum == r1_opnum)
+ || (r2_type == RELOAD_FOR_OUTPUT && r2_opnum >= r1_opnum));
+
case RELOAD_FOR_OPERAND_ADDRESS:
return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN
|| r2_type == RELOAD_FOR_OPERAND_ADDRESS);
case RELOAD_FOR_OUTPUT:
return (r2_type == RELOAD_FOR_INSN || r2_type == RELOAD_FOR_OUTPUT
- || (r2_type == RELOAD_FOR_OUTPUT_ADDRESS
+ || ((r2_type == RELOAD_FOR_OUTPUT_ADDRESS
+ || r2_type == RELOAD_FOR_OUTADDR_ADDRESS)
&& r2_opnum >= r1_opnum));
case RELOAD_FOR_INSN:
int save_reload_spill_index[MAX_RELOADS];
HARD_REG_SET save_reload_reg_used;
HARD_REG_SET save_reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS];
+ HARD_REG_SET save_reload_reg_used_in_inpaddr_addr[MAX_RECOG_OPERANDS];
HARD_REG_SET save_reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS];
+ HARD_REG_SET save_reload_reg_used_in_outaddr_addr[MAX_RECOG_OPERANDS];
HARD_REG_SET save_reload_reg_used_in_input[MAX_RECOG_OPERANDS];
HARD_REG_SET save_reload_reg_used_in_output[MAX_RECOG_OPERANDS];
HARD_REG_SET save_reload_reg_used_in_op_addr;
CLEAR_HARD_REG_SET (reload_reg_used_in_output[i]);
CLEAR_HARD_REG_SET (reload_reg_used_in_input[i]);
CLEAR_HARD_REG_SET (reload_reg_used_in_input_addr[i]);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_inpaddr_addr[i]);
CLEAR_HARD_REG_SET (reload_reg_used_in_output_addr[i]);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_outaddr_addr[i]);
}
#ifdef SMALL_REGISTER_CLASSES
reload_reg_used_in_input[i]);
COPY_HARD_REG_SET (save_reload_reg_used_in_input_addr[i],
reload_reg_used_in_input_addr[i]);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_inpaddr_addr[i],
+ reload_reg_used_in_inpaddr_addr[i]);
COPY_HARD_REG_SET (save_reload_reg_used_in_output_addr[i],
reload_reg_used_in_output_addr[i]);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_outaddr_addr[i],
+ reload_reg_used_in_outaddr_addr[i]);
}
/* If -O, try first with inheritance, then turning it off.
save_reload_reg_used_in_output[i]);
COPY_HARD_REG_SET (reload_reg_used_in_input_addr[i],
save_reload_reg_used_in_input_addr[i]);
+ COPY_HARD_REG_SET (reload_reg_used_in_inpaddr_addr[i],
+ save_reload_reg_used_in_inpaddr_addr[i]);
COPY_HARD_REG_SET (reload_reg_used_in_output_addr[i],
save_reload_reg_used_in_output_addr[i]);
+ COPY_HARD_REG_SET (reload_reg_used_in_outaddr_addr[i],
+ save_reload_reg_used_in_outaddr_addr[i]);
}
}
&& reg_overlap_mentioned_for_reload_p (reload_in[j],
reload_in[i]))
reload_when_needed[j]
- = reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
- ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER;
+ = ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
+ || reload_when_needed[i] == RELOAD_FOR_INPADDR_ADDRESS)
+ ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER);
}
}
}
rtx other_input_address_reload_insns = 0;
rtx other_input_reload_insns = 0;
rtx input_address_reload_insns[MAX_RECOG_OPERANDS];
+ rtx inpaddr_address_reload_insns[MAX_RECOG_OPERANDS];
rtx output_reload_insns[MAX_RECOG_OPERANDS];
rtx output_address_reload_insns[MAX_RECOG_OPERANDS];
+ rtx outaddr_address_reload_insns[MAX_RECOG_OPERANDS];
rtx operand_reload_insns = 0;
rtx other_operand_reload_insns = 0;
rtx other_output_reload_insns[MAX_RECOG_OPERANDS];
for (j = 0; j < reload_n_operands; j++)
input_reload_insns[j] = input_address_reload_insns[j]
+ = inpaddr_address_reload_insns[j]
= output_reload_insns[j] = output_address_reload_insns[j]
+ = outaddr_address_reload_insns[j]
= other_output_reload_insns[j] = 0;
/* Now output the instructions to copy the data into and out of the
case RELOAD_FOR_INPUT_ADDRESS:
where = &input_address_reload_insns[reload_opnum[j]];
break;
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ where = &inpaddr_address_reload_insns[reload_opnum[j]];
+ break;
case RELOAD_FOR_OUTPUT_ADDRESS:
where = &output_address_reload_insns[reload_opnum[j]];
break;
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ where = &outaddr_address_reload_insns[reload_opnum[j]];
+ break;
case RELOAD_FOR_OPERAND_ADDRESS:
where = &operand_reload_insns;
break;
SET_DEST (PATTERN (temp)) = reloadreg;
/* If these are the only uses of the pseudo reg,
pretend for GDB it lives in the reload reg we used. */
- if (reg_n_deaths[REGNO (old)] == 1
- && reg_n_sets[REGNO (old)] == 1)
+ if (REG_N_DEATHS (REGNO (old)) == 1
+ && REG_N_SETS (REGNO (old)) == 1)
{
reg_renumber[REGNO (old)] = REGNO (reload_reg_rtx[j]);
alter_reg (REGNO (old), -1);
RELOAD_OTHER reloads.
- For each operand, any RELOAD_FOR_INPUT_ADDRESS reloads followed by
- the RELOAD_FOR_INPUT reload for the operand.
+ For each operand, any RELOAD_FOR_INPADDR_ADDRESS reloads followed
+ by any RELOAD_FOR_INPUT_ADDRESS reloads followed by the
+ RELOAD_FOR_INPUT reload for the operand.
RELOAD_FOR_OPADDR_ADDRS reloads.
After the insn being reloaded, we write the following:
- For each operand, any RELOAD_FOR_OUTPUT_ADDRESS reload followed by
- the RELOAD_FOR_OUTPUT reload, followed by any RELOAD_OTHER output
- reloads for the operand. The RELOAD_OTHER output reloads are output
- in descending order by reload number. */
+ For each operand, any RELOAD_FOR_OUTADDR_ADDRESS reloads followed
+ by any RELOAD_FOR_OUTPUT_ADDRESS reload followed by the
+ RELOAD_FOR_OUTPUT reload, followed by any RELOAD_OTHER output
+ reloads for the operand. The RELOAD_OTHER output reloads are
+ output in descending order by reload number. */
emit_insns_before (other_input_address_reload_insns, before_insn);
emit_insns_before (other_input_reload_insns, before_insn);
for (j = 0; j < reload_n_operands; j++)
{
+ emit_insns_before (inpaddr_address_reload_insns[j], before_insn);
emit_insns_before (input_address_reload_insns[j], before_insn);
emit_insns_before (input_reload_insns[j], before_insn);
}
for (j = 0; j < reload_n_operands; j++)
{
+ emit_insns_before (outaddr_address_reload_insns[j], following_insn);
emit_insns_before (output_address_reload_insns[j], following_insn);
emit_insns_before (output_reload_insns[j], following_insn);
emit_insns_before (other_output_reload_insns[j], following_insn);
/* See if the pseudo reg has been completely replaced
with reload regs. If so, delete the store insn
and forget we had a stack slot for the pseudo. */
- else if (reg_n_deaths[REGNO (reg)] == 1
- && reg_basic_block[REGNO (reg)] >= 0
+ else if (REG_N_DEATHS (REGNO (reg)) == 1
+ && REG_BASIC_BLOCK (REGNO (reg)) >= 0
&& find_regno_note (insn, REG_DEAD, REGNO (reg)))
{
rtx i2;
}
return count;
}
+\f
+/* This array holds values which are equivalent to a hard register
+ during reload_cse_regs. Each array element is an EXPR_LIST of
+ values. Each time a hard register is set, we set the corresponding
+ array element to the value. Each time a hard register is copied
+ into memory, we add the memory location to the corresponding array
+ element. We don't store values or memory addresses with side
+ effects in this array.
+
+ If the value is a CONST_INT, then the mode of the containing
+ EXPR_LIST is the mode in which that CONST_INT was referenced.
+
+ We sometimes clobber a specific entry in a list. In that case, we
+ just set XEXP (list-entry, 0) to 0. */
+
+static rtx *reg_values;
+
+/* This is a preallocated REG rtx which we use as a temporary in
+ reload_cse_invalidate_regno, so that we don't need to allocate a
+ new one each time through a loop in that function. */
+
+static rtx invalidate_regno_rtx;
+
+/* Invalidate any entries in reg_values which depend on REGNO,
+ including those for REGNO itself. This is called if REGNO is
+ changing. If CLOBBER is true, then always forget anything we
+ currently know about REGNO. MODE is the mode of the assignment to
+ REGNO, which is used to determine how many hard registers are being
+ changed. If MODE is VOIDmode, then only REGNO is being changed;
+ this is used when invalidating call clobbered registers across a
+ call. */
+
+static void
+reload_cse_invalidate_regno (regno, mode, clobber)
+ int regno;
+ enum machine_mode mode;
+ int clobber;
+{
+ int endregno;
+ register int i;
+
+ /* Our callers don't always go through true_regnum; we may see a
+ pseudo-register here from a CLOBBER or the like. We probably
+ won't ever see a pseudo-register that has a real register number,
+ for we check anyhow for safety. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ regno = reg_renumber[regno];
+ if (regno < 0)
+ return;
+
+ if (mode == VOIDmode)
+ endregno = regno + 1;
+ else
+ endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+ if (clobber)
+ for (i = regno; i < endregno; i++)
+ reg_values[i] = 0;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ rtx x;
+
+ for (x = reg_values[i]; x; x = XEXP (x, 1))
+ {
+ if (XEXP (x, 0) != 0
+ && refers_to_regno_p (regno, endregno, XEXP (x, 0), NULL_PTR))
+ {
+ /* If this is the only entry on the list, clear
+ reg_values[i]. Otherwise, just clear this entry on
+ the list. */
+ if (XEXP (x, 1) == 0 && x == reg_values[i])
+ {
+ reg_values[i] = 0;
+ break;
+ }
+ XEXP (x, 0) = 0;
+ }
+ }
+ }
+
+ /* We must look at earlier registers, in case REGNO is part of a
+ multi word value but is not the first register. If an earlier
+ register has a value in a mode which overlaps REGNO, then we must
+ invalidate that earlier register. Note that we do not need to
+ check REGNO or later registers (we must not check REGNO itself,
+ because we would incorrectly conclude that there was a conflict). */
+
+ for (i = 0; i < regno; i++)
+ {
+ rtx x;
+
+ for (x = reg_values[i]; x; x = XEXP (x, 1))
+ {
+ if (XEXP (x, 0) != 0)
+ {
+ PUT_MODE (invalidate_regno_rtx, GET_MODE (x));
+ REGNO (invalidate_regno_rtx) = i;
+ if (refers_to_regno_p (regno, endregno, invalidate_regno_rtx,
+ NULL_PTR))
+ {
+ reload_cse_invalidate_regno (i, VOIDmode, 1);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* The memory at address (plus MEM_BASE MEM_OFFSET), where MEM_OFFSET
+ is a CONST_INT, is being changed. MEM_MODE is the mode of the
+ memory reference. Return whether this change will invalidate VAL. */
+
+static int
+reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, val)
+ rtx mem_base;
+ rtx mem_offset;
+ enum machine_mode mem_mode;
+ rtx val;
+{
+ enum rtx_code code;
+ char *fmt;
+ int i;
+
+ code = GET_CODE (val);
+ switch (code)
+ {
+ /* Get rid of a few simple cases quickly. */
+ case REG:
+ case PC:
+ case CC0:
+ case SCRATCH:
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 0;
+
+ case MEM:
+ {
+ rtx val_base, val_offset;
+
+ if (mem_mode == BLKmode || GET_MODE (val) == BLKmode)
+ return 1;
+
+ val_offset = const0_rtx;
+ val_base = eliminate_constant_term (XEXP (val, 0), &val_offset);
+
+ /* If MEM_BASE and VAL_BASE are the same, but the offsets do
+ not overlap, then we do not have a conflict on this MEM.
+ For complete safety, we still need to check that VAL_BASE
+ itself does not contain an overlapping MEM.
+
+ We can't simplify the check to just OFFSET + SIZE <=
+ OTHER_OFFSET, because SIZE might cause OFFSET to wrap from
+ positive to negative. If we used unsigned arithmetic, we
+ would have the same problem wrapping around zero. */
+
+ if (rtx_equal_p (mem_base, val_base)
+ && ((INTVAL (mem_offset) < INTVAL (val_offset)
+ && (INTVAL (mem_offset) + GET_MODE_SIZE (mem_mode)
+ <= INTVAL (val_offset)))
+ || (INTVAL (val_offset) < INTVAL (mem_offset)
+ && (INTVAL (val_offset) + GET_MODE_SIZE (GET_MODE (val))
+ <= INTVAL (mem_offset)))))
+ return reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode,
+ val_base);
+
+ return 1;
+ }
+
+ default:
+ break;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode,
+ XEXP (val, i)))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ int j;
+
+ for (j = 0; j < XVECLEN (val, i); j++)
+ if (reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode,
+ XVECEXP (val, i, j)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Invalidate any entries in reg_values which are changed because of a
+ store to MEM_RTX. If this is called because of a non-const call
+ instruction, MEM_RTX is (mem:BLK const0_rtx). */
+
+static void
+reload_cse_invalidate_mem (mem_rtx)
+ rtx mem_rtx;
+{
+ register int i;
+ rtx mem_base, mem_offset;
+ enum machine_mode mem_mode;
+
+ /* We detect certain cases where memory addresses can not conflict:
+ if they use the same register, and the offsets do not overlap. */
+
+ mem_offset = const0_rtx;
+ mem_base = eliminate_constant_term (XEXP (mem_rtx, 0), &mem_offset);
+ mem_mode = GET_MODE (mem_rtx);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ rtx x;
+
+ for (x = reg_values[i]; x; x = XEXP (x, 1))
+ {
+ if (XEXP (x, 0) != 0
+ && reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode,
+ XEXP (x, 0)))
+ {
+ /* If this is the only entry on the list, clear
+ reg_values[i]. Otherwise, just clear this entry on
+ the list. */
+ if (XEXP (x, 1) == 0 && x == reg_values[i])
+ {
+ reg_values[i] = 0;
+ break;
+ }
+ XEXP (x, 0) = 0;
+ }
+ }
+ }
+}
+
+/* Invalidate DEST, which is being assigned to or clobbered. The
+ second parameter exists so that this function can be passed to
+ note_stores; it is ignored. */
+
+static void
+reload_cse_invalidate_rtx (dest, ignore)
+ rtx dest;
+ rtx ignore;
+{
+ while (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SUBREG)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == REG)
+ reload_cse_invalidate_regno (REGNO (dest), GET_MODE (dest), 1);
+ else if (GET_CODE (dest) == MEM)
+ reload_cse_invalidate_mem (dest);
+}
+
+/* Do a very simple CSE pass over the hard registers.
+
+ This function detects no-op moves where we happened to assign two
+ different pseudo-registers to the same hard register, and then
+ copied one to the other. Reload will generate a useless
+ instruction copying a register to itself.
+
+ This function also detects cases where we load a value from memory
+ into two different registers, and (if memory is more expensive than
+ registers) changes it to simply copy the first register into the
+ second register. */
+
+static void
+reload_cse_regs (first)
+ rtx first;
+{
+ char *firstobj;
+ rtx callmem;
+ register int i;
+ rtx insn;
+
+ reg_values = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_values[i] = 0;
+
+ /* Create our EXPR_LIST structures on reload_obstack, so that we can
+ free them when we are done. */
+ push_obstacks (&reload_obstack, &reload_obstack);
+ firstobj = (char *) obstack_alloc (&reload_obstack, 0);
+
+ /* We pass this to reload_cse_invalidate_mem to invalidate all of
+ memory for a non-const call instruction. */
+ callmem = gen_rtx (MEM, BLKmode, const0_rtx);
+
+ /* This is used in reload_cse_invalidate_regno to avoid consing a
+ new REG in a loop in that function. */
+ invalidate_regno_rtx = gen_rtx (REG, VOIDmode, 0);
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx body;
+
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ /* Forget all the register values at a code label. We don't
+ try to do anything clever around jumps. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_values[i] = 0;
+
+ continue;
+ }
+
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_values[i] = 0;
+
+ continue;
+ }
+#endif
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ continue;
+
+ /* If this is a call instruction, forget anything stored in a
+ call clobbered register, or, if this is not a const call, in
+ memory. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i])
+ reload_cse_invalidate_regno (i, VOIDmode, 1);
+
+ if (! CONST_CALL_P (insn))
+ reload_cse_invalidate_mem (callmem);
+ }
+
+ body = PATTERN (insn);
+ if (GET_CODE (body) == SET)
+ {
+ if (reload_cse_noop_set_p (body, insn))
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+
+ /* We're done with this insn. */
+ continue;
+ }
+
+ reload_cse_simplify_set (body, insn);
+ reload_cse_record_set (body, body);
+ }
+ else if (GET_CODE (body) == PARALLEL)
+ {
+ int delete;
+
+ /* If every action in a PARALLEL is a noop, we can delete
+ the entire PARALLEL. */
+ for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
+ if (GET_CODE (XVECEXP (body, 0, i)) != SET
+ || ! reload_cse_noop_set_p (XVECEXP (body, 0, i), insn))
+ break;
+ if (i < 0)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+
+ /* We're done with this insn. */
+ continue;
+ }
+
+ /* Look through the PARALLEL and record the values being
+ set, if possible. Also handle any CLOBBERs. */
+ for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
+ {
+ rtx x = XVECEXP (body, 0, i);
+
+ if (GET_CODE (x) == SET)
+ reload_cse_record_set (x, body);
+ else
+ note_stores (x, reload_cse_invalidate_rtx);
+ }
+ }
+ else
+ note_stores (body, reload_cse_invalidate_rtx);
+
+#ifdef AUTO_INC_DEC
+ /* Clobber any registers which appear in REG_INC notes. We
+ could keep track of the changes to their values, but it is
+ unlikely to help. */
+ {
+ rtx x;
+
+ for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
+ if (REG_NOTE_KIND (x) == REG_INC)
+ reload_cse_invalidate_rtx (XEXP (x, 0), NULL_RTX);
+ }
+#endif
+
+ /* Look for any CLOBBERs in CALL_INSN_FUNCTION_USAGE, but only
+ after we have processed the insn. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ rtx x;
+
+ for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
+ if (GET_CODE (XEXP (x, 0)) == CLOBBER)
+ reload_cse_invalidate_rtx (XEXP (XEXP (x, 0), 0), NULL_RTX);
+ }
+ }
+
+ /* Free all the temporary structures we created, and go back to the
+ regular obstacks. */
+ obstack_free (&reload_obstack, firstobj);
+ pop_obstacks ();
+}
+
+/* Return whether the values known for REGNO are equal to VAL. MODE
+ is the mode of the object that VAL is being copied to; this matters
+ if VAL is a CONST_INT. */
+
+static int
+reload_cse_regno_equal_p (regno, val, mode)
+ int regno;
+ rtx val;
+ enum machine_mode mode;
+{
+ rtx x;
+
+ if (val == 0)
+ return 0;
+
+ for (x = reg_values[regno]; x; x = XEXP (x, 1))
+ if (XEXP (x, 0) != 0
+ && rtx_equal_p (XEXP (x, 0), val)
+ && (GET_CODE (val) != CONST_INT
+ || mode == GET_MODE (x)
+ || (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x))
+ /* On a big endian machine if the value spans more than
+ one register then this register holds the high part of
+ it and we can't use it.
+
+ ??? We should also compare with the high part of the
+ value. */
+ && !(WORDS_BIG_ENDIAN
+ && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (GET_MODE (x))))))
+ return 1;
+
+ return 0;
+}
+
+/* See whether a single set is a noop. SET is the set instruction we
+ are should check, and INSN is the instruction from which it came. */
+
+static int
+reload_cse_noop_set_p (set, insn)
+ rtx set;
+ rtx insn;
+{
+ rtx src, dest;
+ enum machine_mode dest_mode;
+ int dreg, sreg;
+ int ret;
+
+ src = SET_SRC (set);
+ dest = SET_DEST (set);
+ dest_mode = GET_MODE (dest);
+
+ if (side_effects_p (src))
+ return 0;
+
+ dreg = true_regnum (dest);
+ sreg = true_regnum (src);
+
+ /* Check for setting a register to itself. In this case, we don't
+ have to worry about REG_DEAD notes. */
+ if (dreg >= 0 && dreg == sreg)
+ return 1;
+
+ ret = 0;
+ if (dreg >= 0)
+ {
+ /* Check for setting a register to itself. */
+ if (dreg == sreg)
+ ret = 1;
+
+ /* Check for setting a register to a value which we already know
+ is in the register. */
+ else if (reload_cse_regno_equal_p (dreg, src, dest_mode))
+ ret = 1;
+
+ /* Check for setting a register DREG to another register SREG
+ where SREG is equal to a value which is already in DREG. */
+ else if (sreg >= 0)
+ {
+ rtx x;
+
+ for (x = reg_values[sreg]; x; x = XEXP (x, 1))
+ {
+ rtx tmp;
+
+ if (XEXP (x, 0) == 0)
+ continue;
+
+ if (dest_mode == GET_MODE (x))
+ tmp = XEXP (x, 0);
+ else if (GET_MODE_BITSIZE (dest_mode)
+ < GET_MODE_BITSIZE (GET_MODE (x)))
+ tmp = gen_lowpart_common (dest_mode, XEXP (x, 0));
+ else
+ continue;
+
+ if (tmp
+ && reload_cse_regno_equal_p (dreg, tmp, dest_mode))
+ {
+ ret = 1;
+ break;
+ }
+ }
+ }
+ }
+ else if (GET_CODE (dest) == MEM)
+ {
+ /* Check for storing a register to memory when we know that the
+ register is equivalent to the memory location. */
+ if (sreg >= 0
+ && reload_cse_regno_equal_p (sreg, dest, dest_mode)
+ && ! side_effects_p (dest))
+ ret = 1;
+ }
+
+ /* If we can delete this SET, then we need to look for an earlier
+ REG_DEAD note on DREG, and remove it if it exists. */
+ if (ret)
+ {
+ if (! find_regno_note (insn, REG_UNUSED, dreg))
+ {
+ rtx trial;
+
+ for (trial = prev_nonnote_insn (insn);
+ (trial
+ && GET_CODE (trial) != CODE_LABEL
+ && GET_CODE (trial) != BARRIER);
+ trial = prev_nonnote_insn (trial))
+ {
+ if (find_regno_note (trial, REG_DEAD, dreg))
+ {
+ remove_death (dreg, trial);
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* Try to simplify a single SET instruction. SET is the set pattern.
+ INSN is the instruction it came from. */
+
+static void
+reload_cse_simplify_set (set, insn)
+ rtx set;
+ rtx insn;
+{
+ int dreg;
+ rtx src;
+ enum machine_mode dest_mode;
+ enum reg_class dclass;
+ register int i;
+
+ /* We only handle one case: if we set a register to a value which is
+ not a register, we try to find that value in some other register
+ and change the set into a register copy. */
+
+ dreg = true_regnum (SET_DEST (set));
+ if (dreg < 0)
+ return;
+
+ src = SET_SRC (set);
+ if (side_effects_p (src) || true_regnum (src) >= 0)
+ return;
+
+ /* If memory loads are cheaper than register copies, don't change
+ them. */
+ if (GET_CODE (src) == MEM && MEMORY_MOVE_COST (GET_MODE (src)) < 2)
+ return;
+
+ dest_mode = GET_MODE (SET_DEST (set));
+ dclass = REGNO_REG_CLASS (dreg);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (i != dreg
+ && REGISTER_MOVE_COST (REGNO_REG_CLASS (i), dclass) == 2
+ && reload_cse_regno_equal_p (i, src, dest_mode))
+ {
+ int validated;
+
+ /* Pop back to the real obstacks while changing the insn. */
+ pop_obstacks ();
+
+ validated = validate_change (insn, &SET_SRC (set),
+ gen_rtx (REG, dest_mode, i), 0);
+
+ /* Go back to the obstack we are using for temporary
+ storage. */
+ push_obstacks (&reload_obstack, &reload_obstack);
+
+ if (validated)
+ {
+ /* We need to look for an earlier REG_DEAD note on I,
+ and remove it if it exists. */
+ if (! find_regno_note (insn, REG_UNUSED, i))
+ {
+ rtx trial;
+
+ for (trial = prev_nonnote_insn (insn);
+ (trial
+ && GET_CODE (trial) != CODE_LABEL
+ && GET_CODE (trial) != BARRIER);
+ trial = prev_nonnote_insn (trial))
+ {
+ if (find_regno_note (trial, REG_DEAD, i))
+ {
+ remove_death (i, trial);
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+ }
+}
+
+/* These two variables are used to pass information from
+ reload_cse_record_set to reload_cse_check_clobber. */
+
+static int reload_cse_check_clobbered;
+static rtx reload_cse_check_src;
+
+/* See if DEST overlaps with RELOAD_CSE_CHECK_SRC. If it does, set
+ RELOAD_CSE_CHECK_CLOBBERED. This is called via note_stores. The
+ second argument, which is passed by note_stores, is ignored. */
+
+static void
+reload_cse_check_clobber (dest, ignore)
+ rtx dest;
+ rtx ignore;
+{
+ if (reg_overlap_mentioned_p (dest, reload_cse_check_src))
+ reload_cse_check_clobbered = 1;
+}
+
+/* Record the result of a SET instruction. SET is the set pattern.
+ BODY is the pattern of the insn that it came from. */
+
+static void
+reload_cse_record_set (set, body)
+ rtx set;
+ rtx body;
+{
+ rtx dest, src, x;
+ int dreg, sreg;
+ enum machine_mode dest_mode;
+
+ dest = SET_DEST (set);
+ src = SET_SRC (set);
+ dreg = true_regnum (dest);
+ sreg = true_regnum (src);
+ dest_mode = GET_MODE (dest);
+
+ /* Some machines don't define AUTO_INC_DEC, but they still use push
+ instructions. We need to catch that case here in order to
+ invalidate the stack pointer correctly. Note that invalidating
+ the stack pointer is different from invalidating DEST. */
+ x = dest;
+ while (GET_CODE (x) == SUBREG
+ || GET_CODE (x) == ZERO_EXTRACT
+ || GET_CODE (x) == SIGN_EXTRACT
+ || GET_CODE (x) == STRICT_LOW_PART)
+ x = XEXP (x, 0);
+ if (push_operand (x, GET_MODE (x)))
+ {
+ reload_cse_invalidate_rtx (stack_pointer_rtx, NULL_RTX);
+ reload_cse_invalidate_rtx (dest, NULL_RTX);
+ return;
+ }
+
+ /* We can only handle an assignment to a register, or a store of a
+ register to a memory location. For other cases, we just clobber
+ the destination. We also have to just clobber if there are side
+ effects in SRC or DEST. */
+ if ((dreg < 0 && GET_CODE (dest) != MEM)
+ || side_effects_p (src)
+ || side_effects_p (dest))
+ {
+ reload_cse_invalidate_rtx (dest, NULL_RTX);
+ return;
+ }
+
+#ifdef HAVE_cc0
+ /* We don't try to handle values involving CC, because it's a pain
+ to keep track of when they have to be invalidated. */
+ if (reg_mentioned_p (cc0_rtx, src)
+ || reg_mentioned_p (cc0_rtx, dest))
+ {
+ reload_cse_invalidate_rtx (dest, NULL_RTX);
+ return;
+ }
+#endif
+
+ /* If BODY is a PARALLEL, then we need to see whether the source of
+ SET is clobbered by some other instruction in the PARALLEL. */
+ if (GET_CODE (body) == PARALLEL)
+ {
+ int i;
+
+ for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
+ {
+ rtx x;
+
+ x = XVECEXP (body, 0, i);
+ if (x == set)
+ continue;
+
+ reload_cse_check_clobbered = 0;
+ reload_cse_check_src = src;
+ note_stores (x, reload_cse_check_clobber);
+ if (reload_cse_check_clobbered)
+ {
+ reload_cse_invalidate_rtx (dest, NULL_RTX);
+ return;
+ }
+ }
+ }
+
+ if (dreg >= 0)
+ {
+ int i;
+
+ /* This is an assignment to a register. Update the value we
+ have stored for the register. */
+ if (sreg >= 0)
+ {
+ rtx x;
+
+ /* This is a copy from one register to another. Any values
+ which were valid for SREG are now valid for DREG. If the
+ mode changes, we use gen_lowpart_common to extract only
+ the part of the value that is copied. */
+ reg_values[dreg] = 0;
+ for (x = reg_values[sreg]; x; x = XEXP (x, 1))
+ {
+ rtx tmp;
+
+ if (XEXP (x, 0) == 0)
+ continue;
+ if (dest_mode == GET_MODE (XEXP (x, 0)))
+ tmp = XEXP (x, 0);
+ else if (GET_MODE_BITSIZE (dest_mode)
+ > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))
+ continue;
+ else
+ tmp = gen_lowpart_common (dest_mode, XEXP (x, 0));
+ if (tmp)
+ reg_values[dreg] = gen_rtx (EXPR_LIST, dest_mode, tmp,
+ reg_values[dreg]);
+ }
+ }
+ else
+ reg_values[dreg] = gen_rtx (EXPR_LIST, dest_mode, src, NULL_RTX);
+
+ /* We've changed DREG, so invalidate any values held by other
+ registers that depend upon it. */
+ reload_cse_invalidate_regno (dreg, dest_mode, 0);
+
+ /* If this assignment changes more than one hard register,
+ forget anything we know about the others. */
+ for (i = 1; i < HARD_REGNO_NREGS (dreg, dest_mode); i++)
+ reg_values[dreg + i] = 0;
+ }
+ else if (GET_CODE (dest) == MEM)
+ {
+ /* Invalidate conflicting memory locations. */
+ reload_cse_invalidate_mem (dest);
+
+ /* If we're storing a register to memory, add DEST to the list
+ in REG_VALUES. */
+ if (sreg >= 0 && ! side_effects_p (dest))
+ reg_values[sreg] = gen_rtx (EXPR_LIST, dest_mode, dest,
+ reg_values[sreg]);
+ }
+ else
+ {
+ /* We should have bailed out earlier. */
+ abort ();
+ }
+}