/* Reload pseudo regs into hard regs for insns that require hard regs.
- Copyright (C) 1987, 88, 89, 92-6, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 92-7 1998 Free Software Foundation, Inc.
This file is part of GNU CC.
Boston, MA 02111-1307, USA. */
-#include <stdio.h>
#include "config.h"
+#include <stdio.h>
#include "rtl.h"
#include "obstack.h"
#include "insn-config.h"
/* This reg set indicates registers that are not good for spill registers.
They will not be used to complete groups of spill registers. This includes
all fixed registers, registers that may be eliminated, and, if
- SMALL_REGISTER_CLASSES is not defined, registers explicitly used in the rtl.
+ SMALL_REGISTER_CLASSES is zero, registers explicitly used in the rtl.
(spill_reg_order prevents these registers from being used to start a
group.) */
static void delete_output_reload PROTO((rtx, int, rtx));
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 int reload_cse_mem_conflict_p PROTO((rtx, 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));
-static void reload_cse_simplify_set PROTO((rtx, rtx));
+static int reload_cse_noop_set_p PROTO((rtx, rtx));
+static int reload_cse_simplify_set PROTO((rtx, rtx));
+static int reload_cse_simplify_operands PROTO((rtx));
static void reload_cse_check_clobber PROTO((rtx, rtx));
static void reload_cse_record_set PROTO((rtx, rtx));
+static void reload_cse_delete_death_notes PROTO((rtx));
+static void reload_cse_no_longer_dead PROTO((int, enum machine_mode));
\f
/* Initialize the reload pass once per compilation. */
but that is hard to determine. */
reload_address_base_reg_class = BASE_REG_CLASS;
reload_address_index_reg_class = INDEX_REG_CLASS;
-#ifdef SMALL_REGISTER_CLASSES
if (SMALL_REGISTER_CLASSES)
{
int regno;
}
indexok:;
}
-#endif /* SMALL_REGISTER_CLASSES */
}
/* Main entry point for the reload pass.
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;
cannot_omit_stores = (char *) alloca (max_regno);
bzero (cannot_omit_stores, max_regno);
-#ifdef SMALL_REGISTER_CLASSES
if (SMALL_REGISTER_CLASSES)
CLEAR_HARD_REG_SET (forbidden_regs);
-#endif
/* Look for REG_EQUIV notes; record what each pseudo is equivalent to.
Also find all paradoxical subregs and find largest such for each pseudo.
if (i > LAST_VIRTUAL_REGISTER)
{
if (GET_CODE (x) == MEM)
- reg_equiv_memory_loc[i] = x;
+ {
+ /* If the operand is a PLUS, the MEM may be shared,
+ so make sure we have an unshared copy here. */
+ if (GET_CODE (XEXP (x, 0)) == PLUS)
+ x = copy_rtx (x);
+
+ reg_equiv_memory_loc[i] = x;
+ }
else if (CONSTANT_P (x))
{
if (LEGITIMATE_CONSTANT_P (x))
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.
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.
rtl as a spill register. But on some, we have to. Those will have
taken care to keep the life of hard regs as short as possible. */
-#ifdef SMALL_REGISTER_CLASSES
if (! SMALL_REGISTER_CLASSES)
-#endif
COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs);
/* Spill any hard regs that we know we can't eliminate. */
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, 0);
+ rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX);
if (strict_memory_address_p (GET_MODE (regno_reg_rtx[i]),
XEXP (x, 0)))
if (num_eliminable)
did_elimination = eliminate_regs_in_insn (insn, 0);
-#ifdef SMALL_REGISTER_CLASSES
/* Set avoid_return_reg if this is an insn
that might use the value of a function call. */
if (SMALL_REGISTER_CLASSES && GET_CODE (insn) == CALL_INSN)
else
after_call = 0;
}
- else if (SMALL_REGISTER_CLASSES
- && after_call != 0
+ else if (SMALL_REGISTER_CLASSES && after_call != 0
&& !(GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == stack_pointer_rtx))
{
avoid_return_reg = after_call;
after_call = 0;
}
-#endif /* SMALL_REGISTER_CLASSES */
/* Analyze the instruction. */
find_reloads (insn, 0, spill_indirect_levels, global,
new_basic_block_needs = 1;
}
-
mode = reload_inmode[i];
if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode))
mode = reload_outmode[i];
for (in_max = 0, out_max = 0, k = 0;
k < reload_n_operands; k++)
{
- 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]);
+ (insn_needs.in_addr[k].regs[j][i]
+ + insn_needs.in_addr_addr[k].regs[j][i]));
out_max
= MAX (out_max, insn_needs.out_addr[k].regs[j][i]);
out_max
}
}
-#ifdef SMALL_REGISTER_CLASSES
/* If this insn stores the value of a function call,
and that value is in a register that has been spilled,
and if the insn needs a reload in a class
}
}
}
-#endif /* SMALL_REGISTER_CLASSES */
/* For each class, collect maximum need of any insn. */
}
}
/* We can't complete a group, so start one. */
-#ifdef SMALL_REGISTER_CLASSES
/* Look for a pair neither of which is explicitly used. */
if (SMALL_REGISTER_CLASSES && i == FIRST_PSEUDO_REGISTER)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
&& ! regs_explicitly_used[j + 1])
break;
}
-#endif
/* Now try any group at all
whose registers are not in bad_spill_regs. */
if (i == FIRST_PSEUDO_REGISTER)
if (! frame_pointer_needed)
for (i = 0; i < n_basic_blocks; i++)
- basic_block_live_at_start[i][HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
- &= ~ ((REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM
- % REGSET_ELT_BITS));
+ CLEAR_REGNO_REG_SET (basic_block_live_at_start[i],
+ HARD_FRAME_POINTER_REGNUM);
/* Come here (with failure set nonzero) if we can't get enough spill regs
and we decide not to abort about it. */
}
}
- /* 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
}
#endif
+ /* If we are doing stack checking, give a warning if this function's
+ frame size is larger than we expect. */
+ if (flag_stack_check && ! STACK_CHECK_BUILTIN)
+ {
+ HOST_WIDE_INT size = get_frame_size () + STACK_CHECK_FIXED_FRAME_SIZE;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_ever_live[i] && ! fixed_regs[i] && call_used_regs[i])
+ size += UNITS_PER_WORD;
+
+ if (size > STACK_CHECK_MAX_FRAME_SIZE)
+ warning ("frame size too large for reliable stack checking");
+ }
+
/* Indicate that we no longer have known memory locations or constants. */
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;
abort (); /* Caller failed to find any register. */
if (fixed_regs[regno] || TEST_HARD_REG_BIT (forbidden_regs, regno))
- fatal ("fixed or forbidden register was spilled.\n\
+ {
+ static char *reg_class_names[] = REG_CLASS_NAMES;
+ fatal ("fixed or forbidden register %d (%s) was spilled for class %s.\n\
This may be due to a compiler bug or to impossible asm\n\
-statements or clauses.");
+statements or clauses.", regno, reg_names[regno], reg_class_names[class]);
+ }
/* Make reg REGNO an additional reload reg. */
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)
{
else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN)
break;
return;
+
+ default:
+ break;
}
/* If we reach here, all eliminations must be at their initial
for (p = reg_eliminate; p < ®_eliminate[NUM_ELIMINABLE_REGS]; p++)
if (p->offset != p->initial_offset)
p->can_eliminate = 0;
+ break;
+
+ default:
+ break;
}
}
\f
the proper thing. */
rtx
-eliminate_regs (x, mem_mode, insn, storing)
+eliminate_regs (x, mem_mode, insn)
rtx x;
enum machine_mode mem_mode;
rtx insn;
- int storing;
{
enum rtx_code code = GET_CODE (x);
struct elim_table *ep;
case RETURN:
return x;
+ case ADDRESSOF:
+ /* This is only for the benefit of the debugging backends, which call
+ eliminate_regs on DECL_RTL; any ADDRESSOFs in the actual insns are
+ removed after CSE. */
+ new = eliminate_regs (XEXP (x, 0), 0, insn);
+ if (GET_CODE (new) == MEM)
+ return XEXP (new, 0);
+ return x;
+
case REG:
regno = REGNO (x);
elimination) and ignore the fact that this is actually a
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, 0);
+ new = eliminate_regs (reg_equiv_memory_loc[regno], mem_mode, insn);
if (new != reg_equiv_memory_loc[regno])
{
cannot_omit_stores[regno] = 1;
reload. This is the desired action. */
{
- rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn, 0);
- rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn, 0);
+ 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))
{
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, 0);
+ rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
rtx new1
- = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn, 0) : 0;
+ = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn) : 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, 0);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
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, 0);
+ new = eliminate_regs (XEXP (x, 1), mem_mode, insn);
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, 0);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
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, 0);
+ mem_mode, insn);
/* 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, 0);
+ new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
if (new != XEXP (x, 0))
{
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
+ if (GET_CODE (new) == MEM
+ && x_size <= new_size
+#ifdef WORD_REGISTER_OPERATIONS
+ /* On these machines, combine can create rtl of the form
+ (set (subreg:m1 (reg:m2 R) 0) ...)
+ where m1 < m2, and expects something interesting to
+ happen to the entire word. Moreover, it will use the
+ (reg:m2 R) later, expecting all bits to be preserved.
+ So if the number of words is the same, preserve the
+ subreg so that push_reloads can see it. */
+ && ! ((x_size-1)/UNITS_PER_WORD == (new_size-1)/UNITS_PER_WORD)
+#endif
#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
- && 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)
+ /* 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, 0);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
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, 0);
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
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, 0);
+ mem_mode, insn);
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, 1);
- rtx new1 = eliminate_regs (SET_SRC (x), 0, insn, 0);
+ rtx new0 = eliminate_regs (SET_DEST (x), 0, insn);
+ rtx new1 = eliminate_regs (SET_SRC (x), 0, insn);
/* If SET_DEST changed from a REG to a MEM and INSN is an insn,
write a CLOBBER insn. */
return x;
case MEM:
+ /* This is only for the benefit of the debugging backends, which call
+ eliminate_regs on DECL_RTL; any ADDRESSOFs in the actual insns are
+ removed after CSE. */
+ if (GET_CODE (XEXP (x, 0)) == ADDRESSOF)
+ return eliminate_regs (XEXP (XEXP (x, 0), 0), 0, 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, 0);
+ new = eliminate_regs (XEXP (x, 0), GET_MODE (x), insn);
if (new != XEXP (x, 0))
{
new = gen_rtx (MEM, GET_MODE (x), new);
}
else
return x;
+
+ default:
+ break;
}
/* Process each of our operands recursively. If any have changed, make a
{
if (*fmt == 'e')
{
- new = eliminate_regs (XEXP (x, i), mem_mode, insn, 0);
+ new = eliminate_regs (XEXP (x, i), mem_mode, insn);
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, 0);
+ new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn);
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, 0);
+ new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX);
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), 0);
+ REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn));
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)
switch (code)
{
case REG:
-#ifdef SMALL_REGISTER_CLASSES
- if (SMALL_REGISTER_CLASSES
- && REGNO (x) < FIRST_PSEUDO_REGISTER
+ if (SMALL_REGISTER_CLASSES && REGNO (x) < FIRST_PSEUDO_REGISTER
&& REG_USERVAR_P (x))
SET_HARD_REG_BIT (forbidden_regs, REGNO (x));
-#endif
return;
case CONST_INT:
reg_max_ref_width[REGNO (SUBREG_REG (x))]
= GET_MODE_SIZE (GET_MODE (x));
return;
+
+ default:
+ break;
}
fmt = GET_RTX_FORMAT (code);
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)
else if (regs_explicitly_used[i])
{
hard_reg_n_uses[i].uses += large + 1;
- /* ??? We are doing this here because of the potential that
- bad code may be generated if a register explicitly used in
- an insn was used as a spill register for that insn. But
- not using these are spill registers may lose on some machine.
- We'll have to see how this works out. */
-#ifdef SMALL_REGISTER_CLASSES
if (! SMALL_REGISTER_CLASSES)
-#endif
+ /* ??? We are doing this here because of the potential
+ that bad code may be generated if a register explicitly
+ used in an insn was used as a spill register for that
+ insn. But not using these are spill registers may lose
+ on some machine. We'll have to see how this works out. */
SET_HARD_REG_BIT (bad_spill_regs, i);
}
}
rtx avoid_return_reg = 0;
rtx oldpat = PATTERN (insn);
-#ifdef SMALL_REGISTER_CLASSES
/* Set avoid_return_reg if this is an insn
that might use the value of a function call. */
if (SMALL_REGISTER_CLASSES && GET_CODE (insn) == CALL_INSN)
else
after_call = 0;
}
- else if (SMALL_REGISTER_CLASSES
- && after_call != 0
+ else if (SMALL_REGISTER_CLASSES && after_call != 0
&& !(GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == stack_pointer_rtx))
{
avoid_return_reg = after_call;
after_call = 0;
}
-#endif /* SMALL_REGISTER_CLASSES */
/* If this is a USE and CLOBBER of a MEM, ensure that any
references to eliminable registers have been removed. */
XEXP (XEXP (PATTERN (insn), 0), 0)
= eliminate_regs (XEXP (XEXP (PATTERN (insn), 0), 0),
GET_MODE (XEXP (PATTERN (insn), 0)),
- NULL_RTX, 0);
+ NULL_RTX);
/* If we need to do register elimination processing, do so.
This might delete the insn, in which case we are done. */
Record the choices of reload reg in reload_reg_rtx. */
choose_reload_regs (insn, avoid_return_reg);
-#ifdef SMALL_REGISTER_CLASSES
/* Merge any reloads that we didn't combine for fear of
increasing the number of spill registers needed but now
discover can be safely merged. */
if (SMALL_REGISTER_CLASSES)
merge_assigned_reloads (insn);
-#endif
/* Generate the insns to reload operands into or out of
their reload regs. */
CLEAR_HARD_REG_SET (reload_reg_used_in_outaddr_addr[i]);
}
-#ifdef SMALL_REGISTER_CLASSES
/* Don't bother with avoiding the return reg
if we have no mandatory reload that could use it. */
if (SMALL_REGISTER_CLASSES && avoid_return_reg)
if (!do_avoid)
avoid_return_reg = 0;
}
-#endif /* SMALL_REGISTER_CLASSES */
#if 0 /* Not needed, now that we can always retry without inheritance. */
/* See if we have more mandatory reloads than spill regs.
unless it is equal to reload_in or reload_out, count such reloads. */
{
- int tem = 0;
-#ifdef SMALL_REGISTER_CLASSES
- if (SMALL_REGISTER_CLASSES)
- tem = (avoid_return_reg != 0);
-#endif
+ int tem = SMALL_REGISTER_CLASSES? (avoid_return_reg != 0): 0;
for (j = 0; j < n_reloads; j++)
if (! reload_optional[j]
&& (reload_in[j] != 0 || reload_out[j] != 0 || reload_secondary_p[j])
}
#endif
-#ifdef SMALL_REGISTER_CLASSES
/* Don't use the subroutine call return reg for a reload
if we are supposed to avoid it. */
if (SMALL_REGISTER_CLASSES && avoid_return_reg)
if (spill_reg_order[r] >= 0)
SET_HARD_REG_BIT (reload_reg_used, r);
}
-#endif /* SMALL_REGISTER_CLASSES */
/* In order to be certain of getting the registers we need,
we must sort the reloads into order of increasing register class.
register int r = reload_order[j];
/* Ignore reloads that got marked inoperative. */
- if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r])
+ if (reload_out[r] == 0 && reload_in[r] == 0
+ && ! reload_secondary_p[r])
continue;
/* If find_reloads chose a to use reload_in or reload_out as a reload
- register, we don't need to chose one. Otherwise, try even if it found
- one since we might save an insn if we find the value lying around. */
+ register, we don't need to chose one. Otherwise, try even if it
+ found one since we might save an insn if we find the value lying
+ around. */
if (reload_in[r] != 0 && reload_reg_rtx[r] != 0
&& (rtx_equal_p (reload_in[r], reload_reg_rtx[r])
|| rtx_equal_p (reload_out[r], reload_reg_rtx[r])))
an object that is already in a register of the desired class.
This would avoid the need for the secondary reload register.
But this is complex because we can't easily determine what
- objects might want to be loaded via this reload. So let a register
- be allocated here. In `emit_reload_insns' we suppress one of the
- loads in the case described above. */
+ objects might want to be loaded via this reload. So let a
+ register be allocated here. In `emit_reload_insns' we suppress
+ one of the loads in the case described above. */
if (inheritance)
{
}
}
\f
-/* If SMALL_REGISTER_CLASSES are defined, we may not have merged two
+/* If SMALL_REGISTER_CLASSES is non-zero, we may not have merged two
reloads of the same item for fear that we might not have enough reload
registers. However, normally they will get the same reload register
and hence actually need not be loaded twice.
This will not increase the number of spill registers needed and will
prevent redundant code. */
-#ifdef SMALL_REGISTER_CLASSES
-
static void
merge_assigned_reloads (insn)
rtx insn;
}
}
}
-#endif /* SMALL_RELOAD_CLASSES */
+
\f
/* Output insns to reload values in and out of the chosen reload regs. */
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);
/* 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 the number of places FIND appears within X, but don't count
an occurrence if some SET_DEST is FIND. */
-static int
+int
count_occurrences (x, find)
register rtx x, find;
{
if (SET_DEST (x) == find)
return count_occurrences (SET_SRC (x), find);
break;
+
+ default:
+ break;
}
format_ptr = GET_RTX_FORMAT (code);
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;
+
+/* This is a set of registers for which we must remove REG_DEAD notes in
+ previous insns, because our modifications made them invalid. That can
+ happen if we introduced the register into the current insn, or we deleted
+ the current insn which used to set the register. */
+
+static HARD_REG_SET no_longer_dead_regs;
+
/* 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
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_RTX))
+ && 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
}
}
}
+
+ /* 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. */
+/* The memory at address MEM_BASE is being changed.
+ Return whether this change will invalidate VAL. */
static int
-reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode, val)
+reload_cse_mem_conflict_p (mem_base, val)
rtx mem_base;
- rtx mem_offset;
- enum machine_mode mem_mode;
rtx val;
{
enum rtx_code code;
{
/* Get rid of a few simple cases quickly. */
case REG:
- case SUBREG:
case PC:
case CC0:
case SCRATCH:
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);
-
+ if (GET_MODE (mem_base) == BLKmode
+ || GET_MODE (val) == BLKmode)
return 1;
- }
+ if (anti_dependence (val, mem_base))
+ return 1;
+ /* The address may contain nested MEMs. */
+ break;
default:
break;
{
if (fmt[i] == 'e')
{
- if (reload_cse_mem_conflict_p (mem_base, mem_offset, mem_mode,
- XEXP (val, i)))
+ if (reload_cse_mem_conflict_p (mem_base, 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)))
+ if (reload_cse_mem_conflict_p (mem_base, XVECEXP (val, i, j)))
return 1;
}
}
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++)
{
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)))
+ && reload_cse_mem_conflict_p (mem_rtx, XEXP (x, 0)))
{
/* If this is the only entry on the list, clear
reg_values[i]. Otherwise, just clear this entry on
reload_cse_invalidate_mem (dest);
}
+/* Possibly delete death notes on the insns before INSN if modifying INSN
+ extended the lifespan of the registers. */
+
+static void
+reload_cse_delete_death_notes (insn)
+ rtx insn;
+{
+ int dreg;
+
+ for (dreg = 0; dreg < FIRST_PSEUDO_REGISTER; dreg++)
+ {
+ rtx trial;
+
+ if (! TEST_HARD_REG_BIT (no_longer_dead_regs, dreg))
+ continue;
+
+ 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;
+ }
+ }
+ }
+}
+
+/* Record that the current insn uses hard reg REGNO in mode MODE. This
+ will be used in reload_cse_delete_death_notes to delete prior REG_DEAD
+ notes for this register. */
+
+static void
+reload_cse_no_longer_dead (regno, mode)
+ int regno;
+ enum machine_mode mode;
+{
+ int nregs = HARD_REGNO_NREGS (regno, mode);
+ while (nregs-- > 0)
+ {
+ SET_HARD_REG_BIT (no_longer_dead_regs, regno);
+ regno++;
+ }
+}
+
+
/* Do a very simple CSE pass over the hard registers.
This function detects no-op moves where we happened to assign two
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. */
+ second register.
-static void
+ Another optimization is performed that scans the operands of each
+ instruction to see whether the value is already available in a
+ hard register. It then replaces the operand with the hard register
+ if possible, much like an optional reload would. */
+
+void
reload_cse_regs (first)
rtx first;
{
register int i;
rtx insn;
+ init_alias_analysis ();
+
reg_values = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
reg_values[i] = 0;
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_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
+ CLEAR_HARD_REG_SET (no_longer_dead_regs);
+
/* If this is a call instruction, forget anything stored in a
call clobbered register, or, if this is not a const call, in
memory. */
body = PATTERN (insn);
if (GET_CODE (body) == SET)
{
- if (reload_cse_noop_set_p (body))
+ int count = 0;
+ if (reload_cse_noop_set_p (body, insn))
{
- /* If we were preserving death notes, then we would want
- to remove any existing death note for the register
- being set. */
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
+ reload_cse_delete_death_notes (insn);
/* We're done with this insn. */
continue;
}
- reload_cse_simplify_set (body, insn);
+ /* It's not a no-op, but we can try to simplify it. */
+ CLEAR_HARD_REG_SET (no_longer_dead_regs);
+ count += reload_cse_simplify_set (body, insn);
+
+ if (count > 0 && apply_change_group ())
+ reload_cse_delete_death_notes (insn);
+ else if (reload_cse_simplify_operands (insn))
+ reload_cse_delete_death_notes (insn);
+
reload_cse_record_set (body, body);
}
else if (GET_CODE (body) == PARALLEL)
{
- int delete;
+ int count = 0;
/* 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)))
+ if ((GET_CODE (XVECEXP (body, 0, i)) != SET
+ || ! reload_cse_noop_set_p (XVECEXP (body, 0, i), insn))
+ && GET_CODE (XVECEXP (body, 0, i)) != CLOBBER)
break;
if (i < 0)
{
- /* If we were preserving death notes, then we would want
- to remove any existing death notes for the registers
- being set. */
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
+ reload_cse_delete_death_notes (insn);
/* We're done with this insn. */
continue;
}
+
+ /* It's not a no-op, but we can try to simplify it. */
+ CLEAR_HARD_REG_SET (no_longer_dead_regs);
+ for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
+ if (GET_CODE (XVECEXP (body, 0, i)) == SET)
+ count += reload_cse_simplify_set (XVECEXP (body, 0, i), insn);
+
+ if (count > 0 && apply_change_group ())
+ reload_cse_delete_death_notes (insn);
+ else if (reload_cse_simplify_operands (insn))
+ reload_cse_delete_death_notes (insn);
/* Look through the PARALLEL and record the values being
set, if possible. Also handle any CLOBBERs. */
&& (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 instruction is a nooop. */
+/* 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)
+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);
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)
- return 1;
+ ret = 1;
/* Check for setting a register to a value which we already know
is in the register. */
- if (reload_cse_regno_equal_p (dreg, src, dest_mode))
- return 1;
+ 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. */
- if (sreg >= 0)
+ else if (sreg >= 0)
{
rtx x;
for (x = reg_values[sreg]; x; x = XEXP (x, 1))
- if (XEXP (x, 0) != 0
- && reload_cse_regno_equal_p (dreg, XEXP (x, 0), dest_mode))
- return 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)
if (sreg >= 0
&& reload_cse_regno_equal_p (sreg, dest, dest_mode)
&& ! side_effects_p (dest))
- return 1;
+ ret = 1;
}
- return 0;
+ /* 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 && dreg >= 0)
+ {
+ if (! find_regno_note (insn, REG_UNUSED, dreg))
+ reload_cse_no_longer_dead (dreg, dest_mode);
+ }
+
+ return ret;
}
/* Try to simplify a single SET instruction. SET is the set pattern.
- INSN is the instruction it came from. */
+ INSN is the instruction it came from.
+ This function only handles 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. */
-static void
+static int
reload_cse_simplify_set (set, insn)
rtx set;
rtx insn;
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;
+ return 0;
src = SET_SRC (set);
if (side_effects_p (src) || true_regnum (src) >= 0)
- return;
+ return 0;
/* 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;
+ return 0;
dest_mode = GET_MODE (SET_DEST (set));
dclass = REGNO_REG_CLASS (dreg);
pop_obstacks ();
validated = validate_change (insn, &SET_SRC (set),
- gen_rtx (REG, dest_mode, i), 0);
+ gen_rtx (REG, dest_mode, i), 1);
/* Go back to the obstack we are using for temporary
storage. */
push_obstacks (&reload_obstack, &reload_obstack);
- if (validated)
- return;
+ if (validated && ! find_regno_note (insn, REG_UNUSED, i))
+ {
+ reload_cse_no_longer_dead (i, dest_mode);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Try to replace operands in INSN with equivalent values that are already
+ in registers. This can be viewed as optional reloading.
+
+ For each non-register operand in the insn, see if any hard regs are
+ known to be equivalent to that operand. Record the alternatives which
+ can accept these hard registers. Among all alternatives, select the
+ ones which are better or equal to the one currently matching, where
+ "better" is in terms of '?' and '!' constraints. Among the remaining
+ alternatives, select the one which replaces most operands with
+ hard registers. */
+
+static int
+reload_cse_simplify_operands (insn)
+ rtx insn;
+{
+#ifdef REGISTER_CONSTRAINTS
+ int insn_code_number, n_operands, n_alternatives;
+ int i,j;
+
+ char *constraints[MAX_RECOG_OPERANDS];
+
+ /* Vector recording how bad an alternative is. */
+ int *alternative_reject;
+ /* Vector recording how many registers can be introduced by choosing
+ this alternative. */
+ int *alternative_nregs;
+ /* Array of vectors recording, for each operand and each alternative,
+ which hard register to substitute, or -1 if the operand should be
+ left as it is. */
+ int *op_alt_regno[MAX_RECOG_OPERANDS];
+ /* Array of alternatives, sorted in order of decreasing desirability. */
+ int *alternative_order;
+
+ /* Find out some information about this insn. */
+ insn_code_number = recog_memoized (insn);
+ /* We don't modify asm instructions. */
+ if (insn_code_number < 0)
+ return 0;
+
+ n_operands = insn_n_operands[insn_code_number];
+ n_alternatives = insn_n_alternatives[insn_code_number];
+
+ if (n_alternatives == 0 || n_operands == 0)
+ return 0;
+ insn_extract (insn);
+
+ /* Figure out which alternative currently matches. */
+ if (! constrain_operands (insn_code_number, 1))
+ abort ();
+
+ alternative_reject = (int *) alloca (n_alternatives * sizeof (int));
+ alternative_nregs = (int *) alloca (n_alternatives * sizeof (int));
+ alternative_order = (int *) alloca (n_alternatives * sizeof (int));
+ bzero ((char *)alternative_reject, n_alternatives * sizeof (int));
+ bzero ((char *)alternative_nregs, n_alternatives * sizeof (int));
+
+ for (i = 0; i < n_operands; i++)
+ {
+ enum machine_mode mode;
+ int regno;
+ char *p;
+
+ op_alt_regno[i] = (int *) alloca (n_alternatives * sizeof (int));
+ for (j = 0; j < n_alternatives; j++)
+ op_alt_regno[i][j] = -1;
+
+ p = constraints[i] = insn_operand_constraint[insn_code_number][i];
+ mode = insn_operand_mode[insn_code_number][i];
+
+ /* Add the reject values for each alternative given by the constraints
+ for this operand. */
+ j = 0;
+ while (*p != '\0')
+ {
+ char c = *p++;
+ if (c == ',')
+ j++;
+ else if (c == '?')
+ alternative_reject[j] += 3;
+ else if (c == '!')
+ alternative_reject[j] += 300;
+ }
+
+ /* We won't change operands which are already registers. We
+ also don't want to modify output operands. */
+ regno = true_regnum (recog_operand[i]);
+ if (regno >= 0
+ || constraints[i][0] == '='
+ || constraints[i][0] == '+')
+ continue;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ int class = (int) NO_REGS;
+
+ if (! reload_cse_regno_equal_p (regno, recog_operand[i], mode))
+ continue;
+
+ /* We found a register equal to this operand. Now look for all
+ alternatives that can accept this register and have not been
+ assigned a register they can use yet. */
+ j = 0;
+ p = constraints[i];
+ for (;;)
+ {
+ char c = *p++;
+
+ switch (c)
+ {
+ case '=': case '+': case '?':
+ case '#': case '&': case '!':
+ case '*': case '%':
+ case '0': case '1': case '2': case '3': case '4':
+ case 'm': case '<': case '>': case 'V': case 'o':
+ case 'E': case 'F': case 'G': case 'H':
+ case 's': case 'i': case 'n':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+#ifdef EXTRA_CONSTRAINT
+ case 'Q': case 'R': case 'S': case 'T': case 'U':
+#endif
+ case 'p': case 'X':
+ /* These don't say anything we care about. */
+ break;
+
+ case 'g': case 'r':
+ class = reg_class_subunion[(int) class][(int) GENERAL_REGS];
+ break;
+
+ default:
+ class
+ = reg_class_subunion[(int) class][(int) REG_CLASS_FROM_LETTER (c)];
+ break;
+
+ case ',': case '\0':
+ /* See if REGNO fits this alternative, and set it up as the
+ replacement register if we don't have one for this
+ alternative yet. */
+ if (op_alt_regno[i][j] == -1
+ && reg_fits_class_p (gen_rtx (REG, mode, regno), class,
+ 0, mode))
+ {
+ alternative_nregs[j]++;
+ op_alt_regno[i][j] = regno;
+ }
+ j++;
+ break;
+ }
+
+ if (c == '\0')
+ break;
+ }
}
}
+
+ /* Record all alternatives which are better or equal to the currently
+ matching one in the alternative_order array. */
+ for (i = j = 0; i < n_alternatives; i++)
+ if (alternative_reject[i] <= alternative_reject[which_alternative])
+ alternative_order[j++] = i;
+ n_alternatives = j;
+
+ /* Sort it. Given a small number of alternatives, a dumb algorithm
+ won't hurt too much. */
+ for (i = 0; i < n_alternatives - 1; i++)
+ {
+ int best = i;
+ int best_reject = alternative_reject[alternative_order[i]];
+ int best_nregs = alternative_nregs[alternative_order[i]];
+ int tmp;
+
+ for (j = i + 1; j < n_alternatives; j++)
+ {
+ int this_reject = alternative_reject[alternative_order[j]];
+ int this_nregs = alternative_nregs[alternative_order[j]];
+
+ if (this_reject < best_reject
+ || (this_reject == best_reject && this_nregs < best_nregs))
+ {
+ best = j;
+ best_reject = this_reject;
+ best_nregs = this_nregs;
+ }
+ }
+
+ tmp = alternative_order[best];
+ alternative_order[best] = alternative_order[i];
+ alternative_order[i] = tmp;
+ }
+
+ /* Substitute the operands as determined by op_alt_regno for the best
+ alternative. */
+ j = alternative_order[0];
+ CLEAR_HARD_REG_SET (no_longer_dead_regs);
+
+ /* Pop back to the real obstacks while changing the insn. */
+ pop_obstacks ();
+
+ for (i = 0; i < n_operands; i++)
+ {
+ enum machine_mode mode = insn_operand_mode[insn_code_number][i];
+ if (op_alt_regno[i][j] == -1)
+ continue;
+
+ reload_cse_no_longer_dead (op_alt_regno[i][j], mode);
+ validate_change (insn, recog_operand_loc[i],
+ gen_rtx (REG, mode, op_alt_regno[i][j]), 1);
+ }
+
+ for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--)
+ {
+ int op = recog_dup_num[i];
+ enum machine_mode mode = insn_operand_mode[insn_code_number][op];
+
+ if (op_alt_regno[op][j] == -1)
+ continue;
+
+ reload_cse_no_longer_dead (op_alt_regno[op][j], mode);
+ validate_change (insn, recog_dup_loc[i],
+ gen_rtx (REG, mode, op_alt_regno[op][j]), 1);
+ }
+
+ /* Go back to the obstack we are using for temporary
+ storage. */
+ push_obstacks (&reload_obstack, &reload_obstack);
+
+ return apply_change_group ();
+#else
+ return 0;
+#endif
}
/* These two variables are used to pass information from
rtx set;
rtx body;
{
- rtx dest, src;
+ rtx dest, src, x;
int dreg, sreg;
enum machine_mode dest_mode;
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
/* This is an assignment to a register. Update the value we
have stored for the register. */
if (sreg >= 0)
- reg_values[dreg] = reg_values[sreg];
+ {
+ 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);