/* Reload pseudo regs into hard regs for insns that require hard regs.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
This file is part of GCC.
#include "expr.h"
#include "optabs.h"
#include "regs.h"
+#include "addresses.h"
#include "basic-block.h"
#include "reload.h"
#include "recog.h"
#include "toplev.h"
#include "except.h"
#include "tree.h"
+#include "target.h"
/* This file contains the reload pass of the compiler, which is
run after register allocation has been done. It checks that
/* Elt N nonzero if reg_last_reload_reg[N] has been set in this insn
for an output reload that stores into reg N. */
-static char *reg_has_output_reload;
+static regset_head reg_has_output_reload;
/* Indicates which hard regs are reload-registers for an output reload
in the current insn. */
/* We allocate reg_equiv_memory_loc inside a varray so that the garbage
collector can keep track of what is inside. */
-varray_type reg_equiv_memory_loc_varray;
+VEC(rtx,gc) *reg_equiv_memory_loc_vec;
/* Element N is the address of stack slot to which pseudo reg N is equivalent.
This is used when the address is not valid as a memory address
or zero if pseudo reg N is not equivalent to a memory slot. */
rtx *reg_equiv_mem;
+/* Element N is an EXPR_LIST of REG_EQUIVs containing MEMs with
+ alternate representations of the location of pseudo reg N. */
+rtx *reg_equiv_alt_mem_list;
+
/* Widest width in which each pseudo reg is referred to (via subreg). */
static unsigned int *reg_max_ref_width;
static void order_regs_for_reload (struct insn_chain *);
static void reload_as_needed (int);
static void forget_old_reloads_1 (rtx, rtx, void *);
+static void forget_marked_reloads (regset);
static int reload_reg_class_lower (const void *, const void *);
static void mark_reload_reg_in_use (unsigned int, int, enum reload_type,
enum machine_mode);
static void copy_eh_notes (rtx, rtx);
static int reloads_conflict (int, int);
static rtx gen_reload (rtx, rtx, int, enum reload_type);
+static rtx emit_insn_if_valid_for_reload (rtx);
\f
/* Initialize the reload pass once per compilation. */
INIT_REG_SET (&spilled_pseudos);
INIT_REG_SET (&pseudos_counted);
- VARRAY_RTX_INIT (reg_equiv_memory_loc_varray, 0, "reg_equiv_memory_loc");
}
/* List of insn chains that are currently unused. */
Record memory equivalents in reg_mem_equiv so they can
be substituted eventually by altering the REG-rtx's. */
- reg_equiv_constant = xcalloc (max_regno, sizeof (rtx));
- reg_equiv_invariant = xcalloc (max_regno, sizeof (rtx));
- reg_equiv_mem = xcalloc (max_regno, sizeof (rtx));
- reg_equiv_address = xcalloc (max_regno, sizeof (rtx));
- reg_max_ref_width = xcalloc (max_regno, sizeof (int));
- reg_old_renumber = xcalloc (max_regno, sizeof (short));
+ reg_equiv_constant = XCNEWVEC (rtx, max_regno);
+ reg_equiv_invariant = XCNEWVEC (rtx, max_regno);
+ reg_equiv_mem = XCNEWVEC (rtx, max_regno);
+ reg_equiv_alt_mem_list = XCNEWVEC (rtx, max_regno);
+ reg_equiv_address = XCNEWVEC (rtx, max_regno);
+ reg_max_ref_width = XCNEWVEC (unsigned int, max_regno);
+ reg_old_renumber = XCNEWVEC (short, max_regno);
memcpy (reg_old_renumber, reg_renumber, max_regno * sizeof (short));
- pseudo_forbidden_regs = xmalloc (max_regno * sizeof (HARD_REG_SET));
- pseudo_previous_regs = xcalloc (max_regno, sizeof (HARD_REG_SET));
+ pseudo_forbidden_regs = XNEWVEC (HARD_REG_SET, max_regno);
+ pseudo_previous_regs = XCNEWVEC (HARD_REG_SET, max_regno);
CLEAR_HARD_REG_SET (bad_spill_regs_global);
/* 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. */
- offsets_known_at = xmalloc (num_labels);
- offsets_at = xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
+ offsets_known_at = XNEWVEC (char, num_labels);
+ offsets_at = (HOST_WIDE_INT (*)[NUM_ELIMINABLE_REGS]) xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
/* Alter each pseudo-reg rtx to contain its hard reg number.
Assign stack slots to the pseudos that lack hard regs or equivalents.
{
int something_changed;
int did_spill;
-
HOST_WIDE_INT starting_frame_size;
- /* Round size of stack frame to stack_alignment_needed. 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. */
- if (cfun->stack_alignment_needed)
- assign_stack_local (BLKmode, 0, cfun->stack_alignment_needed);
-
starting_frame_size = get_frame_size ();
set_initial_elim_offsets ();
/* If we allocated another stack slot, redo elimination bookkeeping. */
if (starting_frame_size != get_frame_size ())
continue;
+ if (starting_frame_size && cfun->stack_alignment_needed)
+ {
+ /* If we have a stack frame, we must align it now. The
+ stack size may be a part of the offset computation for
+ register elimination. So if this changes the stack size,
+ then repeat the elimination bookkeeping. We don't
+ realign when there is no stack, as that will cause a
+ stack frame when none is needed should
+ STARTING_FRAME_OFFSET not be already aligned to
+ STACK_BOUNDARY. */
+ assign_stack_local (BLKmode, 0, cfun->stack_alignment_needed);
+ if (starting_frame_size != get_frame_size ())
+ continue;
+ }
if (caller_save_needed)
{
HARD_REG_SET to_spill;
CLEAR_HARD_REG_SET (to_spill);
update_eliminables (&to_spill);
+ AND_COMPL_HARD_REG_SET (used_spill_regs, to_spill);
+
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (to_spill, i))
{
{
rtx *pnote;
+ /* Clean up invalid ASMs so that they don't confuse later passes.
+ See PR 21299. */
+ if (asm_noperands (PATTERN (insn)) >= 0)
+ {
+ extract_insn (insn);
+ if (!constrain_operands (1))
+ {
+ error_for_asm (insn,
+ "%<asm%> operand has impossible constraints");
+ delete_insn (insn);
+ continue;
+ }
+ }
+
if (CALL_P (insn))
replace_pseudos_in (& CALL_INSN_FUNCTION_USAGE (insn),
VOIDmode, CALL_INSN_FUNCTION_USAGE (insn));
free (reg_equiv_invariant);
reg_equiv_constant = 0;
reg_equiv_invariant = 0;
- VARRAY_GROW (reg_equiv_memory_loc_varray, 0);
+ VEC_free (rtx, gc, reg_equiv_memory_loc_vec);
reg_equiv_memory_loc = 0;
if (offsets_known_at)
if (offsets_at)
free (offsets_at);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (reg_equiv_alt_mem_list[i])
+ free_EXPR_LIST_list (®_equiv_alt_mem_list[i]);
+ free (reg_equiv_alt_mem_list);
+
free (reg_equiv_mem);
reg_equiv_init = 0;
free (reg_equiv_address);
case 'p':
cls = (int) reg_class_subunion[cls]
- [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+ [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
break;
case 'g':
default:
if (EXTRA_ADDRESS_CONSTRAINT (c, p))
cls = (int) reg_class_subunion[cls]
- [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+ [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
else
cls = (int) reg_class_subunion[cls]
[(int) REG_CLASS_FROM_CONSTRAINT (c, p)];
&& rld[r].regno == -1)
if (! find_reg (chain, i))
{
+ if (dump_file)
+ fprintf (dump_file, "reload failure for reload %d\n", r);
spill_failure (chain->insn, rld[r].class);
failure = 1;
return;
{
error ("unable to find a register to spill in class %qs",
reg_class_names[class]);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "\nReloads for insn # %d\n", INSN_UID (insn));
+ debug_reload_to_stream (dump_file);
+ }
fatal_insn ("this is the insn:", insn);
}
}
&& reg_equiv_memory_loc[i] == 0)
{
rtx x;
+ enum machine_mode mode = GET_MODE (regno_reg_rtx[i]);
unsigned int inherent_size = PSEUDO_REGNO_BYTES (i);
+ unsigned int inherent_align = GET_MODE_ALIGNMENT (mode);
unsigned int total_size = MAX (inherent_size, reg_max_ref_width[i]);
+ unsigned int min_align = reg_max_ref_width[i] * BITS_PER_UNIT;
int adjust = 0;
/* Each pseudo reg has an inherent size which comes from its own mode,
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,
- inherent_size == total_size ? 0 : -1);
+ x = assign_stack_local (mode, total_size,
+ min_align > inherent_align
+ || total_size > inherent_size ? -1 : 0);
if (BYTES_BIG_ENDIAN)
/* Cancel the big-endian correction done in assign_stack_local.
Get the address of the beginning of the slot.
else if (spill_stack_slot[from_reg] != 0
&& spill_stack_slot_width[from_reg] >= total_size
&& (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg]))
- >= inherent_size))
+ >= inherent_size)
+ && MEM_ALIGN (spill_stack_slot[from_reg]) >= min_align)
x = spill_stack_slot[from_reg];
/* Allocate a bigger slot. */
{
/* Compute maximum size needed, both for inherent size
and for total size. */
- enum machine_mode mode = GET_MODE (regno_reg_rtx[i]);
rtx stack_slot;
if (spill_stack_slot[from_reg])
mode = GET_MODE (spill_stack_slot[from_reg]);
if (spill_stack_slot_width[from_reg] > total_size)
total_size = spill_stack_slot_width[from_reg];
+ if (MEM_ALIGN (spill_stack_slot[from_reg]) > min_align)
+ min_align = MEM_ALIGN (spill_stack_slot[from_reg]);
}
/* Make a slot with that size. */
x = assign_stack_local (mode, total_size,
- inherent_size == total_size ? 0 : -1);
+ min_align > inherent_align
+ || total_size > inherent_size ? -1 : 0);
stack_slot = x;
/* All pseudos mapped to this slot can alias each other. */
case CTZ:
case POPCOUNT:
case PARITY:
+ case BSWAP:
new = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, false);
if (new != XEXP (x, 0))
return gen_rtx_fmt_e (code, GET_MODE (x), new);
new = eliminate_regs_1 (XEXP (x, i), mem_mode, insn, false);
if (new != XEXP (x, i) && ! copied)
{
- rtx new_x = rtx_alloc (code);
- memcpy (new_x, x, RTX_SIZE (code));
- x = new_x;
+ x = shallow_copy_rtx (x);
copied = 1;
}
XEXP (x, i) = new;
XVEC (x, i)->elem);
if (! copied)
{
- rtx new_x = rtx_alloc (code);
- memcpy (new_x, x, RTX_SIZE (code));
- x = new_x;
+ x = shallow_copy_rtx (x);
copied = 1;
}
XVEC (x, i) = new_v;
case CTZ:
case POPCOUNT:
case PARITY:
+ case BSWAP:
elimination_effects (XEXP (x, 0), mem_mode);
return;
{
if (GET_CODE (SET_SRC (old_set)) == PLUS)
plus_src = SET_SRC (old_set);
- /* First see if the source is of the form (plus (reg) CST). */
+ /* First see if the source is of the form (plus (...) CST). */
if (plus_src
- && REG_P (XEXP (plus_src, 0))
- && GET_CODE (XEXP (plus_src, 1)) == CONST_INT
- && REGNO (XEXP (plus_src, 0)) < FIRST_PSEUDO_REGISTER)
+ && GET_CODE (XEXP (plus_src, 1)) == CONST_INT)
plus_cst_src = plus_src;
else if (REG_P (SET_SRC (old_set))
|| plus_src)
{
/* Otherwise, see if we have a REG_EQUAL note of the form
- (plus (reg) CST). */
+ (plus (...) CST). */
rtx links;
for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
{
if (REG_NOTE_KIND (links) == REG_EQUAL
&& GET_CODE (XEXP (links, 0)) == PLUS
- && REG_P (XEXP (XEXP (links, 0), 0))
- && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT
- && REGNO (XEXP (XEXP (links, 0), 0)) < FIRST_PSEUDO_REGISTER)
+ && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT)
{
plus_cst_src = XEXP (links, 0);
break;
}
}
}
+
+ /* Check that the first operand of the PLUS is a hard reg or
+ the lowpart subreg of one. */
+ if (plus_cst_src)
+ {
+ rtx reg = XEXP (plus_cst_src, 0);
+ if (GET_CODE (reg) == SUBREG && subreg_lowpart_p (reg))
+ reg = SUBREG_REG (reg);
+
+ if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ plus_cst_src = 0;
+ }
}
if (plus_cst_src)
{
rtx reg = XEXP (plus_cst_src, 0);
HOST_WIDE_INT offset = INTVAL (XEXP (plus_cst_src, 1));
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
if (ep->from_rtx == reg && ep->can_eliminate)
{
+ rtx to_rtx = ep->to_rtx;
offset += ep->offset;
+ offset = trunc_int_for_mode (offset, GET_MODE (reg));
- if (offset == 0)
- {
- int num_clobbers;
- /* We assume here that if we need a PARALLEL with
- CLOBBERs for this assignment, we can do with the
- MATCH_SCRATCHes that add_clobbers allocates.
- There's not much we can do if that doesn't work. */
- PATTERN (insn) = gen_rtx_SET (VOIDmode,
- SET_DEST (old_set),
- ep->to_rtx);
- num_clobbers = 0;
- INSN_CODE (insn) = recog (PATTERN (insn), insn, &num_clobbers);
- if (num_clobbers)
- {
- rtvec vec = rtvec_alloc (num_clobbers + 1);
-
- vec->elem[0] = PATTERN (insn);
- PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, vec);
- add_clobbers (PATTERN (insn), INSN_CODE (insn));
- }
- gcc_assert (INSN_CODE (insn) >= 0);
- }
+ if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG)
+ to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)),
+ to_rtx);
/* If we have a nonzero offset, and the source is already
a simple REG, the following transformation would
increase the cost of the insn by replacing a simple REG
with (plus (reg sp) CST). So try only when we already
had a PLUS before. */
- else if (plus_src)
+ if (offset == 0 || plus_src)
{
+ rtx new_src = plus_constant (to_rtx, offset);
+
new_body = old_body;
if (! replace)
{
PATTERN (insn) = new_body;
old_set = single_set (insn);
- XEXP (SET_SRC (old_set), 0) = ep->to_rtx;
- XEXP (SET_SRC (old_set), 1) = GEN_INT (offset);
+ /* First see if this insn remains valid when we make the
+ change. If not, try to replace the whole pattern with
+ a simple set (this may help if the original insn was a
+ PARALLEL that was only recognized as single_set due to
+ REG_UNUSED notes). If this isn't valid either, keep
+ the INSN_CODE the same and let reload fix it up. */
+ if (!validate_change (insn, &SET_SRC (old_set), new_src, 0))
+ {
+ rtx new_pat = gen_rtx_SET (VOIDmode,
+ SET_DEST (old_set), new_src);
+
+ if (!validate_change (insn, &PATTERN (insn), new_pat, 0))
+ SET_SRC (old_set) = new_src;
+ }
}
else
break;
|| GET_CODE (SET_SRC (old_set)) == PLUS))
{
int new_icode = recog (PATTERN (insn), insn, 0);
- if (new_icode < 0)
- INSN_CODE (insn) = icode;
+ if (new_icode >= 0)
+ INSN_CODE (insn) = new_icode;
}
}
case SUBREG:
if (REG_P (SUBREG_REG (x))
- && GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ && (GET_MODE_SIZE (GET_MODE (x))
+ > reg_max_ref_width[REGNO (SUBREG_REG (x))]))
reg_max_ref_width[REGNO (SUBREG_REG (x))]
= GET_MODE_SIZE (GET_MODE (x));
return;
memset (spill_reg_rtx, 0, sizeof spill_reg_rtx);
memset (spill_reg_store, 0, sizeof spill_reg_store);
- reg_last_reload_reg = xcalloc (max_regno, sizeof (rtx));
- reg_has_output_reload = xmalloc (max_regno);
+ reg_last_reload_reg = XCNEWVEC (rtx, max_regno);
+ INIT_REG_SET (®_has_output_reload);
CLEAR_HARD_REG_SET (reg_reloaded_valid);
CLEAR_HARD_REG_SET (reg_reloaded_call_part_clobbered);
else if (INSN_P (insn))
{
- rtx oldpat = copy_rtx (PATTERN (insn));
+ regset_head regs_to_forget;
+ INIT_REG_SET (®s_to_forget);
+ note_stores (PATTERN (insn), forget_old_reloads_1, ®s_to_forget);
/* If this is a USE and CLOBBER of a MEM, ensure that any
references to eliminable registers have been removed. */
if (NOTE_P (insn))
{
update_eliminable_offsets ();
+ CLEAR_REG_SET (®s_to_forget);
continue;
}
}
rtx's for those pseudo regs. */
else
{
- memset (reg_has_output_reload, 0, max_regno);
+ CLEAR_REG_SET (®_has_output_reload);
CLEAR_HARD_REG_SET (reg_is_output_reload);
find_reloads (insn, 1, spill_indirect_levels, live_known,
for this insn in order to be stored in
(obeying register constraints). That is correct; such reload
registers ARE still valid. */
- note_stores (oldpat, forget_old_reloads_1, NULL);
+ forget_marked_reloads (®s_to_forget);
+ CLEAR_REG_SET (®s_to_forget);
/* There may have been CLOBBER insns placed after INSN. So scan
between INSN and NEXT and use them to forget old reloads. */
the reload for inheritance. */
SET_HARD_REG_BIT (reg_is_output_reload,
REGNO (reload_reg));
- reg_has_output_reload[REGNO (XEXP (in_reg, 0))] = 1;
+ SET_REGNO_REG_SET (®_has_output_reload,
+ REGNO (XEXP (in_reg, 0)));
}
else
forget_old_reloads_1 (XEXP (in_reg, 0), NULL_RTX,
{
SET_HARD_REG_BIT (reg_is_output_reload,
REGNO (rld[i].reg_rtx));
- reg_has_output_reload[REGNO (XEXP (in_reg, 0))] = 1;
+ SET_REGNO_REG_SET (®_has_output_reload,
+ REGNO (XEXP (in_reg, 0)));
}
}
}
/* Clean up. */
free (reg_last_reload_reg);
- free (reg_has_output_reload);
+ CLEAR_REG_SET (®_has_output_reload);
}
/* Discard all record of any value reloaded from X,
unless X is an output reload reg of the current insn.
X may be a hard reg (the reload reg)
- or it may be a pseudo reg that was reloaded from. */
+ or it may be a pseudo reg that was reloaded from.
+
+ When DATA is non-NULL just mark the registers in regset
+ to be forgotten later. */
static void
forget_old_reloads_1 (rtx x, rtx ignored ATTRIBUTE_UNUSED,
- void *data ATTRIBUTE_UNUSED)
+ void *data)
{
unsigned int regno;
unsigned int nr;
+ regset regs = (regset) data;
/* note_stores does give us subregs of hard regs,
subreg_regno_offset requires a hard reg. */
This can happen if a block-local pseudo is allocated to that reg
and it wasn't spilled because this block's total need is 0.
Then some insn might have an optional reload and use this reg. */
- for (i = 0; i < nr; i++)
- /* But don't do this if the reg actually serves as an output
- reload reg in the current instruction. */
+ if (!regs)
+ for (i = 0; i < nr; i++)
+ /* But don't do this if the reg actually serves as an output
+ reload reg in the current instruction. */
+ if (n_reloads == 0
+ || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))
+ {
+ CLEAR_HARD_REG_BIT (reg_reloaded_valid, regno + i);
+ CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, regno + i);
+ spill_reg_store[regno + i] = 0;
+ }
+ }
+
+ if (regs)
+ while (nr-- > 0)
+ SET_REGNO_REG_SET (regs, regno + nr);
+ else
+ {
+ /* Since value of X has changed,
+ forget any value previously copied from it. */
+
+ while (nr-- > 0)
+ /* But don't forget a copy if this is the output reload
+ that establishes the copy's validity. */
if (n_reloads == 0
- || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))
+ || !REGNO_REG_SET_P (®_has_output_reload, regno + nr))
+ reg_last_reload_reg[regno + nr] = 0;
+ }
+}
+
+/* Forget the reloads marked in regset by previous function. */
+static void
+forget_marked_reloads (regset regs)
+{
+ unsigned int reg;
+ reg_set_iterator rsi;
+ EXECUTE_IF_SET_IN_REG_SET (regs, 0, reg, rsi)
+ {
+ if (reg < FIRST_PSEUDO_REGISTER
+ /* But don't do this if the reg actually serves as an output
+ reload reg in the current instruction. */
+ && (n_reloads == 0
+ || ! TEST_HARD_REG_BIT (reg_is_output_reload, reg)))
{
- CLEAR_HARD_REG_BIT (reg_reloaded_valid, regno + i);
- CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, regno + i);
- spill_reg_store[regno + i] = 0;
+ CLEAR_HARD_REG_BIT (reg_reloaded_valid, reg);
+ CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, reg);
+ spill_reg_store[reg] = 0;
}
+ if (n_reloads == 0
+ || !REGNO_REG_SET_P (®_has_output_reload, reg))
+ reg_last_reload_reg[reg] = 0;
}
-
- /* Since value of X has changed,
- forget any value previously copied from it. */
-
- while (nr-- > 0)
- /* But don't forget a copy if this is the output reload
- that establishes the copy's validity. */
- if (n_reloads == 0 || reg_has_output_reload[regno + nr] == 0)
- reg_last_reload_reg[regno + nr] = 0;
}
\f
/* The following HARD_REG_SETs indicate when each hard register is
}
}
\f
+
+/* Returns whether R1 and R2 are uniquely chained: the value of one
+ is used by the other, and that value is not used by any other
+ reload for this insn. This is used to partially undo the decision
+ made in find_reloads when in the case of multiple
+ RELOAD_FOR_OPERAND_ADDRESS reloads it converts all
+ RELOAD_FOR_OPADDR_ADDR reloads into RELOAD_FOR_OPERAND_ADDRESS
+ reloads. This code tries to avoid the conflict created by that
+ change. It might be cleaner to explicitly keep track of which
+ RELOAD_FOR_OPADDR_ADDR reload is associated with which
+ RELOAD_FOR_OPERAND_ADDRESS reload, rather than to try to detect
+ this after the fact. */
+static bool
+reloads_unique_chain_p (int r1, int r2)
+{
+ int i;
+
+ /* We only check input reloads. */
+ if (! rld[r1].in || ! rld[r2].in)
+ return false;
+
+ /* Avoid anything with output reloads. */
+ if (rld[r1].out || rld[r2].out)
+ return false;
+
+ /* "chained" means one reload is a component of the other reload,
+ not the same as the other reload. */
+ if (rld[r1].opnum != rld[r2].opnum
+ || rtx_equal_p (rld[r1].in, rld[r2].in)
+ || rld[r1].optional || rld[r2].optional
+ || ! (reg_mentioned_p (rld[r1].in, rld[r2].in)
+ || reg_mentioned_p (rld[r2].in, rld[r1].in)))
+ return false;
+
+ for (i = 0; i < n_reloads; i ++)
+ /* Look for input reloads that aren't our two */
+ if (i != r1 && i != r2 && rld[i].in)
+ {
+ /* If our reload is mentioned at all, it isn't a simple chain. */
+ if (reg_mentioned_p (rld[r1].in, rld[i].in))
+ return false;
+ }
+ return true;
+}
+
/* Return 1 if the reloads denoted by R1 and R2 cannot share a register.
Return 0 otherwise.
case RELOAD_FOR_OPERAND_ADDRESS:
return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN
- || r2_type == RELOAD_FOR_OPERAND_ADDRESS);
+ || (r2_type == RELOAD_FOR_OPERAND_ADDRESS
+ && !reloads_unique_chain_p (r1, r2)));
case RELOAD_FOR_OPADDR_ADDR:
return (r2_type == RELOAD_FOR_INPUT
else if (GET_CODE (rld[r].in_reg) == SUBREG
&& REG_P (SUBREG_REG (rld[r].in_reg)))
{
- byte = SUBREG_BYTE (rld[r].in_reg);
regno = REGNO (SUBREG_REG (rld[r].in_reg));
if (regno < FIRST_PSEUDO_REGISTER)
regno = subreg_regno (rld[r].in_reg);
+ else
+ byte = SUBREG_BYTE (rld[r].in_reg);
mode = GET_MODE (rld[r].in_reg);
}
#ifdef AUTO_INC_DEC
- else if ((GET_CODE (rld[r].in_reg) == PRE_INC
- || GET_CODE (rld[r].in_reg) == PRE_DEC
- || GET_CODE (rld[r].in_reg) == POST_INC
- || GET_CODE (rld[r].in_reg) == POST_DEC)
+ else if (GET_RTX_CLASS (GET_CODE (rld[r].in_reg)) == RTX_AUTOINC
&& REG_P (XEXP (rld[r].in_reg, 0)))
{
regno = REGNO (XEXP (rld[r].in_reg, 0));
regno = subreg_regno (rld[r].in);
#endif
- if (regno >= 0 && reg_last_reload_reg[regno] != 0)
+ if (regno >= 0
+ && reg_last_reload_reg[regno] != 0
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ /* Verify that the register it's in can be used in
+ mode MODE. */
+ && !REG_CANNOT_CHANGE_MODE_P (REGNO (reg_last_reload_reg[regno]),
+ GET_MODE (reg_last_reload_reg[regno]),
+ mode)
+#endif
+ )
{
enum reg_class class = rld[r].class, last_class;
rtx last_reg = reg_last_reload_reg[regno];
if ((GET_MODE_SIZE (GET_MODE (last_reg))
>= GET_MODE_SIZE (need_mode))
-#ifdef CANNOT_CHANGE_MODE_CLASS
- /* Verify that the register in "i" can be obtained
- from LAST_REG. */
- && !REG_CANNOT_CHANGE_MODE_P (REGNO (last_reg),
- GET_MODE (last_reg),
- mode)
-#endif
&& reg_reloaded_contents[i] == regno
&& TEST_HARD_REG_BIT (reg_reloaded_valid, i)
&& HARD_REGNO_MODE_OK (i, rld[r].mode)
enough. */
|| ((REGISTER_MOVE_COST (mode, last_class, class)
< MEMORY_MOVE_COST (mode, class, 1))
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
- && (SECONDARY_INPUT_RELOAD_CLASS (class, mode,
- last_reg)
+ && (secondary_reload_class (1, class, mode,
+ last_reg)
== NO_REGS)
-#endif
#ifdef SECONDARY_MEMORY_NEEDED
&& ! SECONDARY_MEMORY_NEEDED (last_class, class,
mode)
if (equiv != 0)
{
- if (regno_clobbered_p (regno, insn, rld[r].mode, 0))
+ if (regno_clobbered_p (regno, insn, rld[r].mode, 2))
switch (rld[r].when_needed)
{
case RELOAD_FOR_OTHER_ADDRESS:
nr = hard_regno_nregs[nregno][rld[r].mode];
while (--nr >= 0)
- reg_has_output_reload[nregno + nr] = 1;
+ SET_REGNO_REG_SET (®_has_output_reload,
+ nregno + nr);
if (i >= 0)
{
static rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER];
static HARD_REG_SET reg_reloaded_died;
+/* Check if *RELOAD_REG is suitable as an intermediate or scratch register
+ of class NEW_CLASS with mode NEW_MODE. Or alternatively, if alt_reload_reg
+ is nonzero, if that is suitable. On success, change *RELOAD_REG to the
+ adjusted register, and return true. Otherwise, return false. */
+static bool
+reload_adjust_reg_for_temp (rtx *reload_reg, rtx alt_reload_reg,
+ enum reg_class new_class,
+ enum machine_mode new_mode)
+
+{
+ rtx reg;
+
+ for (reg = *reload_reg; reg; reg = alt_reload_reg, alt_reload_reg = 0)
+ {
+ unsigned regno = REGNO (reg);
+
+ if (!TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], regno))
+ continue;
+ if (GET_MODE (reg) != new_mode)
+ {
+ if (!HARD_REGNO_MODE_OK (regno, new_mode))
+ continue;
+ if (hard_regno_nregs[regno][new_mode]
+ > hard_regno_nregs[regno][GET_MODE (reg)])
+ continue;
+ reg = reload_adjust_reg_for_mode (reg, new_mode);
+ }
+ *reload_reg = reg;
+ return true;
+ }
+ return false;
+}
+
+/* Check if *RELOAD_REG is suitable as a scratch register for the reload
+ pattern with insn_code ICODE, or alternatively, if alt_reload_reg is
+ nonzero, if that is suitable. On success, change *RELOAD_REG to the
+ adjusted register, and return true. Otherwise, return false. */
+static bool
+reload_adjust_reg_for_icode (rtx *reload_reg, rtx alt_reload_reg,
+ enum insn_code icode)
+
+{
+ enum reg_class new_class = scratch_reload_class (icode);
+ enum machine_mode new_mode = insn_data[(int) icode].operand[2].mode;
+
+ return reload_adjust_reg_for_temp (reload_reg, alt_reload_reg,
+ new_class, new_mode);
+}
+
/* Generate insns to perform reload RL, which is for the insn in CHAIN and
has the number J. OLD contains the value to be used as input. */
if (mode == VOIDmode)
mode = rl->inmode;
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
- /* If we need a secondary register for this operation, see if
- the value is already in a register in that class. Don't
- do this if the secondary register will be used as a scratch
- register. */
-
- if (rl->secondary_in_reload >= 0
- && rl->secondary_in_icode == CODE_FOR_nothing
- && optimize)
- oldequiv
- = find_equiv_reg (old, insn,
- rld[rl->secondary_in_reload].class,
- -1, NULL, 0, mode);
-#endif
-
- /* If reloading from memory, see if there is a register
- that already holds the same value. If so, reload from there.
- We can pass 0 as the reload_reg_p argument because
- any other reload has either already been emitted,
- in which case find_equiv_reg will see the reload-insn,
- or has yet to be emitted, in which case it doesn't matter
- because we will use this equiv reg right away. */
-
- if (oldequiv == 0 && optimize
- && (MEM_P (old)
- || (REG_P (old)
- && REGNO (old) >= FIRST_PSEUDO_REGISTER
- && reg_renumber[REGNO (old)] < 0)))
- oldequiv = find_equiv_reg (old, insn, ALL_REGS, -1, NULL, 0, mode);
-
- if (oldequiv)
- {
- unsigned int regno = true_regnum (oldequiv);
-
- /* Don't use OLDEQUIV if any other reload changes it at an
- earlier stage of this insn or at this stage. */
- if (! free_for_value_p (regno, rl->mode, rl->opnum, rl->when_needed,
- rl->in, const0_rtx, j, 0))
- oldequiv = 0;
-
- /* If it is no cheaper to copy from OLDEQUIV into the
- reload register than it would be to move from memory,
- don't use it. Likewise, if we need a secondary register
- or memory. */
-
- if (oldequiv != 0
- && (((enum reg_class) REGNO_REG_CLASS (regno) != rl->class
- && (REGISTER_MOVE_COST (mode, REGNO_REG_CLASS (regno),
- rl->class)
- >= MEMORY_MOVE_COST (mode, rl->class, 1)))
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
- || (SECONDARY_INPUT_RELOAD_CLASS (rl->class,
- mode, oldequiv)
- != NO_REGS)
-#endif
-#ifdef SECONDARY_MEMORY_NEEDED
- || SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (regno),
- rl->class,
- mode)
-#endif
- ))
- oldequiv = 0;
- }
-
/* delete_output_reload is only invoked properly if old contains
the original pseudo register. Since this is replaced with a
hard reg when RELOAD_OVERRIDE_IN is set, see if we can
find the pseudo in RELOAD_IN_REG. */
- if (oldequiv == 0
- && reload_override_in[j]
+ if (reload_override_in[j]
&& REG_P (rl->in_reg))
{
oldequiv = old;
/* We can't do that, so output an insn to load RELOADREG. */
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
/* If we have a secondary reload, pick up the secondary register
and icode, if any. If OLDEQUIV and OLD are different or
if this is an in-out reload, recompute whether or not we
if (! special && rl->secondary_in_reload >= 0)
{
rtx second_reload_reg = 0;
+ rtx third_reload_reg = 0;
int secondary_reload = rl->secondary_in_reload;
rtx real_oldequiv = oldequiv;
rtx real_old = old;
rtx tmp;
enum insn_code icode;
+ enum insn_code tertiary_icode = CODE_FOR_nothing;
/* If OLDEQUIV is a pseudo with a MEM, get the real MEM
and similarly for OLD.
}
second_reload_reg = rld[secondary_reload].reg_rtx;
+ if (rld[secondary_reload].secondary_in_reload >= 0)
+ {
+ int tertiary_reload = rld[secondary_reload].secondary_in_reload;
+
+ third_reload_reg = rld[tertiary_reload].reg_rtx;
+ tertiary_icode = rld[secondary_reload].secondary_in_icode;
+ /* We'd have to add more code for quartary reloads. */
+ gcc_assert (rld[tertiary_reload].secondary_in_reload < 0);
+ }
icode = rl->secondary_in_icode;
if ((old != oldequiv && ! rtx_equal_p (old, oldequiv))
|| (rl->in != 0 && rl->out != 0))
{
- enum reg_class new_class
- = SECONDARY_INPUT_RELOAD_CLASS (rl->class,
- mode, real_oldequiv);
+ secondary_reload_info sri, sri2;
+ enum reg_class new_class, new_t_class;
+
+ sri.icode = CODE_FOR_nothing;
+ sri.prev_sri = NULL;
+ new_class = targetm.secondary_reload (1, real_oldequiv, rl->class,
+ mode, &sri);
- if (new_class == NO_REGS)
+ if (new_class == NO_REGS && sri.icode == CODE_FOR_nothing)
second_reload_reg = 0;
- else
+ else if (new_class == NO_REGS)
{
- enum insn_code new_icode;
- enum machine_mode new_mode;
-
- if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class],
- REGNO (second_reload_reg)))
- oldequiv = old, real_oldequiv = real_old;
+ if (reload_adjust_reg_for_icode (&second_reload_reg,
+ third_reload_reg, sri.icode))
+ icode = sri.icode, third_reload_reg = 0;
else
+ oldequiv = old, real_oldequiv = real_old;
+ }
+ else if (sri.icode != CODE_FOR_nothing)
+ /* We currently lack a way to express this in reloads. */
+ gcc_unreachable ();
+ else
+ {
+ sri2.icode = CODE_FOR_nothing;
+ sri2.prev_sri = &sri;
+ new_t_class = targetm.secondary_reload (1, real_oldequiv,
+ new_class, mode, &sri);
+ if (new_t_class == NO_REGS && sri2.icode == CODE_FOR_nothing)
{
- new_icode = reload_in_optab[(int) mode];
- if (new_icode != CODE_FOR_nothing
- && ((insn_data[(int) new_icode].operand[0].predicate
- && ! ((*insn_data[(int) new_icode].operand[0].predicate)
- (reloadreg, mode)))
- || (insn_data[(int) new_icode].operand[1].predicate
- && ! ((*insn_data[(int) new_icode].operand[1].predicate)
- (real_oldequiv, mode)))))
- new_icode = CODE_FOR_nothing;
-
- if (new_icode == CODE_FOR_nothing)
- new_mode = mode;
+ if (reload_adjust_reg_for_temp (&second_reload_reg,
+ third_reload_reg,
+ new_class, mode))
+ third_reload_reg = 0, tertiary_icode = sri2.icode;
else
- new_mode = insn_data[(int) new_icode].operand[2].mode;
+ oldequiv = old, real_oldequiv = real_old;
+ }
+ else if (new_t_class == NO_REGS && sri2.icode != CODE_FOR_nothing)
+ {
+ rtx intermediate = second_reload_reg;
- if (GET_MODE (second_reload_reg) != new_mode)
+ if (reload_adjust_reg_for_temp (&intermediate, NULL,
+ new_class, mode)
+ && reload_adjust_reg_for_icode (&third_reload_reg, NULL,
+ sri2.icode))
{
- if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg),
- new_mode))
- oldequiv = old, real_oldequiv = real_old;
- else
- second_reload_reg
- = reload_adjust_reg_for_mode (second_reload_reg,
- new_mode);
+ second_reload_reg = intermediate;
+ tertiary_icode = sri2.icode;
}
+ else
+ oldequiv = old, real_oldequiv = real_old;
}
+ else if (new_t_class != NO_REGS && sri2.icode == CODE_FOR_nothing)
+ {
+ rtx intermediate = second_reload_reg;
+
+ if (reload_adjust_reg_for_temp (&intermediate, NULL,
+ new_class, mode)
+ && reload_adjust_reg_for_temp (&third_reload_reg, NULL,
+ new_t_class, mode))
+ {
+ second_reload_reg = intermediate;
+ tertiary_icode = sri2.icode;
+ }
+ else
+ oldequiv = old, real_oldequiv = real_old;
+ }
+ else
+ /* This could be handled more intelligently too. */
+ oldequiv = old, real_oldequiv = real_old;
}
}
{
if (icode != CODE_FOR_nothing)
{
+ /* We'd have to add extra code to handle this case. */
+ gcc_assert (!third_reload_reg);
+
emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv,
second_reload_reg));
special = 1;
{
/* See if we need a scratch register to load the
intermediate register (a tertiary reload). */
- enum insn_code tertiary_icode
- = rld[secondary_reload].secondary_in_icode;
-
if (tertiary_icode != CODE_FOR_nothing)
{
- rtx third_reload_reg
- = rld[rld[secondary_reload].secondary_in_reload].reg_rtx;
-
emit_insn ((GEN_FCN (tertiary_icode)
(second_reload_reg, real_oldequiv,
third_reload_reg)));
}
+ else if (third_reload_reg)
+ {
+ gen_reload (third_reload_reg, real_oldequiv,
+ rl->opnum,
+ rl->when_needed);
+ gen_reload (second_reload_reg, third_reload_reg,
+ rl->opnum,
+ rl->when_needed);
+ }
else
gen_reload (second_reload_reg, real_oldequiv,
rl->opnum,
}
}
}
-#endif
if (! special && ! rtx_equal_p (reloadreg, oldequiv))
{
if (GET_MODE (reloadreg) != mode)
reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
-#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
-
/* If we need two reload regs, set RELOADREG to the intermediate
one, since it will be stored into OLD. We might need a secondary
register only for an input reload, so check again here. */
if (rl->secondary_out_reload >= 0)
{
rtx real_old = old;
+ int secondary_reload = rl->secondary_out_reload;
+ int tertiary_reload = rld[secondary_reload].secondary_out_reload;
if (REG_P (old) && REGNO (old) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_mem[REGNO (old)] != 0)
real_old = reg_equiv_mem[REGNO (old)];
- if ((SECONDARY_OUTPUT_RELOAD_CLASS (rl->class,
- mode, real_old)
- != NO_REGS))
+ if (secondary_reload_class (0, rl->class, mode, real_old) != NO_REGS)
{
rtx second_reloadreg = reloadreg;
- reloadreg = rld[rl->secondary_out_reload].reg_rtx;
+ reloadreg = rld[secondary_reload].reg_rtx;
/* See if RELOADREG is to be used as a scratch register
or as an intermediate register. */
if (rl->secondary_out_icode != CODE_FOR_nothing)
{
+ /* We'd have to add extra code to handle this case. */
+ gcc_assert (tertiary_reload < 0);
+
emit_insn ((GEN_FCN (rl->secondary_out_icode)
(real_old, second_reloadreg, reloadreg)));
special = 1;
/* See if we need both a scratch and intermediate reload
register. */
- int secondary_reload = rl->secondary_out_reload;
enum insn_code tertiary_icode
= rld[secondary_reload].secondary_out_icode;
+ /* We'd have to add more code for quartary reloads. */
+ gcc_assert (tertiary_reload < 0
+ || rld[tertiary_reload].secondary_out_reload < 0);
+
if (GET_MODE (reloadreg) != mode)
reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
if (tertiary_icode != CODE_FOR_nothing)
{
- rtx third_reloadreg
- = rld[rld[secondary_reload].secondary_out_reload].reg_rtx;
+ rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
rtx tem;
/* Copy primary reload reg to secondary reload reg.
}
else
- /* Copy between the reload regs here and then to
- OUT later. */
+ {
+ /* Copy between the reload regs here and then to
+ OUT later. */
- gen_reload (reloadreg, second_reloadreg,
- rl->opnum, rl->when_needed);
+ gen_reload (reloadreg, second_reloadreg,
+ rl->opnum, rl->when_needed);
+ if (tertiary_reload >= 0)
+ {
+ rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
+
+ gen_reload (third_reloadreg, reloadreg,
+ rl->opnum, rl->when_needed);
+ reloadreg = third_reloadreg;
+ }
+ }
}
}
}
-#endif
/* Output the last reload insn. */
if (! special)
return;
/* If is a JUMP_INSN, we can't support output reloads yet. */
- gcc_assert (!JUMP_P (insn));
+ gcc_assert (NONJUMP_INSN_P (insn));
emit_output_reload_insns (chain, rld + j, j);
}
if (REG_P (reg)
&& REGNO (reg) >= FIRST_PSEUDO_REGISTER
- && ! reg_has_output_reload[REGNO (reg)])
+ && !REGNO_REG_SET_P (®_has_output_reload, REGNO (reg)))
{
int nregno = REGNO (reg);
&& rld[r].in != 0
&& ((REG_P (rld[r].in)
&& REGNO (rld[r].in) >= FIRST_PSEUDO_REGISTER
- && ! reg_has_output_reload[REGNO (rld[r].in)])
+ && !REGNO_REG_SET_P (®_has_output_reload,
+ REGNO (rld[r].in)))
|| (REG_P (rld[r].in_reg)
- && ! reg_has_output_reload[REGNO (rld[r].in_reg)]))
+ && !REGNO_REG_SET_P (®_has_output_reload,
+ REGNO (rld[r].in_reg))))
&& ! reg_set_p (rld[r].reg_rtx, PATTERN (insn)))
{
int nregno;
/* If a register gets output-reloaded from a non-spill register,
that invalidates any previous reloaded copy of it.
But forget_old_reloads_1 won't get to see it, because
- it thinks only about the original insn. So invalidate it here. */
- if (i < 0 && rld[r].out != 0
- && (REG_P (rld[r].out)
- || (MEM_P (rld[r].out)
+ it thinks only about the original insn. So invalidate it here.
+ Also do the same thing for RELOAD_OTHER constraints where the
+ output is discarded. */
+ if (i < 0
+ && ((rld[r].out != 0
+ && (REG_P (rld[r].out)
+ || (MEM_P (rld[r].out)
+ && REG_P (rld[r].out_reg))))
+ || (rld[r].out == 0 && rld[r].out_reg
&& REG_P (rld[r].out_reg))))
{
- rtx out = (REG_P (rld[r].out)
+ rtx out = ((rld[r].out && REG_P (rld[r].out))
? rld[r].out : rld[r].out_reg);
int nregno = REGNO (out);
+
+ /* REG_RTX is now set or clobbered by the main instruction.
+ As the comment above explains, forget_old_reloads_1 only
+ sees the original instruction, and there is no guarantee
+ that the original instruction also clobbered REG_RTX.
+ For example, if find_reloads sees that the input side of
+ a matched operand pair dies in this instruction, it may
+ use the input register as the reload register.
+
+ Calling forget_old_reloads_1 is a waste of effort if
+ REG_RTX is also the output register.
+
+ If we know that REG_RTX holds the value of a pseudo
+ register, the code after the call will record that fact. */
+ if (rld[r].reg_rtx && rld[r].reg_rtx != out)
+ forget_old_reloads_1 (rld[r].reg_rtx, NULL_RTX, NULL);
+
if (nregno >= FIRST_PSEUDO_REGISTER)
{
rtx src_reg, store_insn = NULL_RTX;
/* We have to set reg_has_output_reload here, or else
forget_old_reloads_1 will clear reg_last_reload_reg
right away. */
- reg_has_output_reload[nregno] = 1;
+ SET_REGNO_REG_SET (®_has_output_reload,
+ nregno);
}
}
else
{
- int num_regs = hard_regno_nregs[nregno][GET_MODE (rld[r].out)];
+ int num_regs = hard_regno_nregs[nregno][GET_MODE (out)];
while (num_regs-- > 0)
reg_last_reload_reg[nregno + num_regs] = 0;
IOR_HARD_REG_SET (reg_reloaded_dead, reg_reloaded_died);
}
\f
+/* Go through the motions to emit INSN and test if it is strictly valid.
+ Return the emitted insn if valid, else return NULL. */
+
+static rtx
+emit_insn_if_valid_for_reload (rtx insn)
+{
+ rtx last = get_last_insn ();
+ int code;
+
+ insn = emit_insn (insn);
+ code = recog_memoized (insn);
+
+ if (code >= 0)
+ {
+ extract_insn (insn);
+ /* We want constrain operands to treat this insn strictly in its
+ validity determination, i.e., the way it would after reload has
+ completed. */
+ if (constrain_operands (1))
+ return insn;
+ }
+
+ delete_insns_since (last);
+ return NULL;
+}
+
/* Emit code to perform a reload from IN (which may be a reload register) to
OUT (which may also be a reload register). IN or OUT is from operand
OPNUM with reload type TYPE.
trying to emit a single insn to perform the add. If it is not valid,
we use a two insn sequence.
+ Or we can be asked to reload an unary operand that was a fragment of
+ an addressing mode, into a register. If it isn't recognized as-is,
+ we try making the unop operand and the reload-register the same:
+ (set reg:X (unop:X expr:Y))
+ -> (set reg:Y expr:Y) (set reg:X (unop:X reg:Y)).
+
Finally, we could be called to handle an 'o' constraint by putting
an address into a register. In that case, we first try to do this
with a named pattern of "reload_load_address". If no such pattern
if (op0 != XEXP (in, 0) || op1 != XEXP (in, 1))
in = gen_rtx_PLUS (GET_MODE (in), op0, op1);
- insn = emit_insn (gen_rtx_SET (VOIDmode, out, in));
- code = recog_memoized (insn);
-
- if (code >= 0)
- {
- extract_insn (insn);
- /* We want constrain operands to treat this insn strictly in
- its validity determination, i.e., the way it would after reload
- has completed. */
- if (constrain_operands (1))
- return insn;
- }
-
- delete_insns_since (last);
+ insn = emit_insn_if_valid_for_reload (gen_rtx_SET (VOIDmode, out, in));
+ if (insn)
+ return insn;
/* If that failed, we must use a conservative two-insn sequence.
if (rtx_equal_p (op0, op1))
op1 = out;
- insn = emit_insn (gen_add2_insn (out, op1));
-
- /* If that failed, copy the address register to the reload register.
- Then add the constant to the reload register. */
-
- code = recog_memoized (insn);
-
- if (code >= 0)
+ insn = emit_insn_if_valid_for_reload (gen_add2_insn (out, op1));
+ if (insn)
{
- extract_insn (insn);
- /* We want constrain operands to treat this insn strictly in
- its validity determination, i.e., the way it would after reload
- has completed. */
- if (constrain_operands (1))
- {
- /* Add a REG_EQUIV note so that find_equiv_reg can find it. */
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
- return insn;
- }
+ /* Add a REG_EQUIV note so that find_equiv_reg can find it. */
+ set_unique_reg_note (insn, REG_EQUIV, in);
+ return insn;
}
- delete_insns_since (last);
+ /* If that failed, copy the address register to the reload register.
+ Then add the constant to the reload register. */
gen_reload (out, op1, opnum, type);
insn = emit_insn (gen_add2_insn (out, op0));
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+ set_unique_reg_note (insn, REG_EQUIV, in);
}
#ifdef SECONDARY_MEMORY_NEEDED
gen_reload (out, loc, opnum, type);
}
#endif
+ else if (REG_P (out) && UNARY_P (in))
+ {
+ rtx insn;
+ rtx op1;
+ rtx out_moded;
+ rtx set;
+
+ op1 = find_replacement (&XEXP (in, 0));
+ if (op1 != XEXP (in, 0))
+ in = gen_rtx_fmt_e (GET_CODE (in), GET_MODE (in), op1);
+
+ /* First, try a plain SET. */
+ set = emit_insn_if_valid_for_reload (gen_rtx_SET (VOIDmode, out, in));
+ if (set)
+ return set;
+ /* If that failed, move the inner operand to the reload
+ register, and try the same unop with the inner expression
+ replaced with the reload register. */
+
+ if (GET_MODE (op1) != GET_MODE (out))
+ out_moded = gen_rtx_REG (GET_MODE (op1), REGNO (out));
+ else
+ out_moded = out;
+
+ gen_reload (out_moded, op1, opnum, type);
+
+ insn
+ = gen_rtx_SET (VOIDmode, out,
+ gen_rtx_fmt_e (GET_CODE (in), GET_MODE (in),
+ out_moded));
+ insn = emit_insn_if_valid_for_reload (insn);
+ if (insn)
+ {
+ set_unique_reg_note (insn, REG_EQUIV, in);
+ return insn;
+ }
+
+ fatal_insn ("Failure trying to reload:", set);
+ }
/* If IN is a simple operand, use gen_move_insn. */
else if (OBJECT_P (in) || GET_CODE (in) == SUBREG)
- emit_insn (gen_move_insn (out, in));
+ {
+ tem = emit_insn (gen_move_insn (out, in));
+ /* IN may contain a LABEL_REF, if so add a REG_LABEL note. */
+ mark_jump_label (in, tem, 0);
+ }
#ifdef HAVE_reload_load_address
else if (HAVE_reload_load_address)
}
}
n_occurrences = count_occurrences (PATTERN (insn), reg, 0);
+ if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
+ n_occurrences += count_occurrences (CALL_INSN_FUNCTION_USAGE (insn),
+ reg, 0);
if (substed)
n_occurrences += count_occurrences (PATTERN (insn),
eliminate_regs (substed, 0,
NULL_RTX), 0);
+ for (i1 = reg_equiv_alt_mem_list [REGNO (reg)]; i1; i1 = XEXP (i1, 1))
+ {
+ gcc_assert (!rtx_equal_p (XEXP (i1, 0), substed));
+ n_occurrences += count_occurrences (PATTERN (insn), XEXP (i1, 0), 0);
+ }
if (n_occurrences > n_inherited)
return;
inc_for_reload (rtx reloadreg, rtx in, rtx value, int inc_amount)
{
/* REG or MEM to be copied and incremented. */
- rtx incloc = XEXP (value, 0);
+ rtx incloc = find_replacement (&XEXP (value, 0));
/* Nonzero if increment after copying. */
- int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC);
+ int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC
+ || GET_CODE (value) == POST_MODIFY);
rtx last;
rtx inc;
rtx add_insn;
int code;
rtx store;
- rtx real_in = in == value ? XEXP (in, 0) : in;
+ rtx real_in = in == value ? incloc : in;
/* No hard register is equivalent to this register after
inc/dec operation. If REG_LAST_RELOAD_REG were nonzero,
if (REG_P (incloc))
reg_last_reload_reg[REGNO (incloc)] = 0;
- if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
- inc_amount = -inc_amount;
+ if (GET_CODE (value) == PRE_MODIFY || GET_CODE (value) == POST_MODIFY)
+ {
+ gcc_assert (GET_CODE (XEXP (value, 1)) == PLUS);
+ inc = find_replacement (&XEXP (XEXP (value, 1), 1));
+ }
+ else
+ {
+ if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
+ inc_amount = -inc_amount;
- inc = GEN_INT (inc_amount);
+ inc = GEN_INT (inc_amount);
+ }
/* If this is post-increment, first copy the location to the reload reg. */
if (post && real_in != reloadreg)
emit_insn (gen_add2_insn (reloadreg, inc));
store = emit_insn (gen_move_insn (incloc, reloadreg));
- emit_insn (gen_add2_insn (reloadreg, GEN_INT (-inc_amount)));
+ if (GET_CODE (inc) == CONST_INT)
+ emit_insn (gen_add2_insn (reloadreg, GEN_INT (-INTVAL (inc))));
+ else
+ emit_insn (gen_sub2_insn (reloadreg, inc));
}
return store;