/* Search an insn for pseudo regs that must be in hard regs and are not.
- Copyright (C) 1987, 88, 89, 92-7, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 92-97, 1998 Free Software Foundation, Inc.
This file is part of GNU CC.
#define REG_OK_STRICT
#include "config.h"
-#include <stdio.h>
+#include "system.h"
#include "rtl.h"
#include "insn-config.h"
#include "insn-codes.h"
#include "real.h"
#include "output.h"
#include "expr.h"
+#include "toplev.h"
#ifndef REGISTER_MOVE_COST
#define REGISTER_MOVE_COST(x, y) 2
reload_optional char, nonzero for an optional reload.
Optional reloads are ignored unless the
value is already sitting in a register.
+ reload_nongroup char, nonzero when a reload must use a register
+ not already allocated to a group.
reload_inc int, positive amount to increment or decrement by if
reload_in is a PRE_DEC, PRE_INC, POST_DEC, POST_INC.
Ignored otherwise (don't assume it is zero).
enum machine_mode reload_outmode[MAX_RELOADS];
rtx reload_reg_rtx[MAX_RELOADS];
char reload_optional[MAX_RELOADS];
+char reload_nongroup[MAX_RELOADS];
int reload_inc[MAX_RELOADS];
rtx reload_in_reg[MAX_RELOADS];
char reload_nocombine[MAX_RELOADS];
? RELOAD_FOR_OUTADDR_ADDRESS \
: (type)))
+#ifdef HAVE_SECONDARY_RELOADS
static int push_secondary_reload PROTO((int, rtx, int, int, enum reg_class,
enum machine_mode, enum reload_type,
enum insn_code *));
+#endif
static enum reg_class find_valid_class PROTO((enum machine_mode, int));
static int push_reload PROTO((rtx, rtx, rtx *, rtx *, enum reg_class,
enum machine_mode, enum machine_mode,
reload_outmode[t_reload] = ! in_p ? t_mode : VOIDmode;
reload_reg_rtx[t_reload] = 0;
reload_optional[t_reload] = optional;
+ reload_nongroup[t_reload] = 0;
reload_inc[t_reload] = 0;
/* Maybe we could combine these, but it seems too tricky. */
reload_nocombine[t_reload] = 1;
reload_outmode[s_reload] = ! in_p ? mode : VOIDmode;
reload_reg_rtx[s_reload] = 0;
reload_optional[s_reload] = optional;
+ reload_nongroup[s_reload] = 0;
reload_inc[s_reload] = 0;
/* Maybe we could combine these, but it seems too tricky. */
reload_nocombine[s_reload] = 1;
reload_outmode[i] = outmode;
reload_reg_rtx[i] = 0;
reload_optional[i] = optional;
+ reload_nongroup[i] = 0;
reload_inc[i] = 0;
reload_nocombine[i] = 0;
reload_in_reg[i] = inloc ? *inloc : 0;
replacements[i].what = to;
}
\f
+/* Remove all replacements in reload FROM. */
+void
+remove_replacements (from)
+ int from;
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < n_replacements; i++)
+ {
+ if (replacements[i].what == from)
+ continue;
+ replacements[j++] = replacements[i];
+ }
+}
+\f
/* If there is only one output reload, and it is not for an earlyclobber
operand, try to combine it with a (logically unrelated) input reload
to reduce the number of reload registers needed.
int goal_alternative_swapped;
int best;
int commutative;
+ int changed;
char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS];
rtx substed_operand[MAX_RECOG_OPERANDS];
rtx body = PATTERN (insn);
substed_operand[i] = recog_operand[i] = *recog_operand_loc[i];
}
else if (code == SUBREG)
- substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]
- = find_reloads_toplev (recog_operand[i], i, address_type[i],
- ind_levels,
- set != 0
- && &SET_DEST (set) == recog_operand_loc[i]);
+ {
+ rtx reg = SUBREG_REG (recog_operand[i]);
+ rtx op
+ = find_reloads_toplev (recog_operand[i], i, address_type[i],
+ ind_levels,
+ set != 0
+ && &SET_DEST (set) == recog_operand_loc[i]);
+
+ /* If we made a MEM to load (a part of) the stackslot of a pseudo
+ that didn't get a hard register, emit a USE with a REG_EQUAL
+ note in front so that we might inherit a previous, possibly
+ wider reload. */
+
+ if (GET_CODE (op) == MEM
+ && GET_CODE (reg) == REG
+ && (GET_MODE_SIZE (GET_MODE (reg))
+ >= GET_MODE_SIZE (GET_MODE (op))))
+ REG_NOTES (emit_insn_before (gen_rtx_USE (VOIDmode, reg), insn))
+ = gen_rtx_EXPR_LIST (REG_EQUAL,
+ reg_equiv_memory_loc[REGNO (reg)], NULL_RTX);
+
+ substed_operand[i] = recog_operand[i] = *recog_operand_loc[i] = op;
+ }
else if (code == PLUS || GET_RTX_CLASS (code) == '1')
/* We can get a PLUS as an "operand" as a result of register
elimination. See eliminate_regs and gen_reload. We handle
register int regno = REGNO (recog_operand[i]);
if (reg_equiv_constant[regno] != 0
&& (set == 0 || &SET_DEST (set) != recog_operand_loc[i]))
- substed_operand[i] = recog_operand[i]
- = reg_equiv_constant[regno];
+ {
+ /* Record the existing mode so that the check if constants are
+ allowed will work when operand_mode isn't specified. */
+
+ if (operand_mode[i] == VOIDmode)
+ operand_mode[i] = GET_MODE (recog_operand[i]);
+
+ substed_operand[i] = recog_operand[i]
+ = reg_equiv_constant[regno];
+ }
#if 0 /* This might screw code in reload1.c to delete prior output-reload
that feeds this insn. */
if (reg_equiv_mem[regno] != 0)
if (rtx_varies_p (address))
address = copy_rtx (address);
- /* If this is an output operand, we must output a CLOBBER
- after INSN so find_equiv_reg knows REGNO is being written.
- Mark this insn specially, do we can put our output reloads
- after it. */
-
- if (modified[i] != RELOAD_READ)
- PUT_MODE (emit_insn_after (gen_rtx_CLOBBER (VOIDmode,
- recog_operand[i]),
- insn),
- DImode);
+ /* Emit a USE that shows what register is being used/modified. */
+ REG_NOTES (emit_insn_before (gen_rtx_USE (VOIDmode,
+ recog_operand[i]),
+ insn))
+ = gen_rtx_EXPR_LIST (REG_EQUAL,
+ reg_equiv_memory_loc[regno],
+ NULL_RTX);
*recog_operand_loc[i] = recog_operand[i]
= gen_rtx_MEM (GET_MODE (recog_operand[i]), address);
or got the wrong kind of hard reg. For this, we must consider
all the operands together against the register constraints. */
- best = MAX_RECOG_OPERANDS + 300;
+ best = MAX_RECOG_OPERANDS * 2 + 600;
swapped = 0;
goal_alternative_swapped = 0;
int earlyclobber = 0;
/* If the predicate accepts a unary operator, it means that
- we need to reload the operand. */
- if (GET_RTX_CLASS (GET_CODE (operand)) == '1')
+ we need to reload the operand, but do not do this for
+ match_operator and friends. */
+ if (GET_RTX_CLASS (GET_CODE (operand)) == '1' && *p != 0)
operand = XEXP (operand, 0);
/* If the operand is a SUBREG, extract
{
offset += SUBREG_WORD (operand);
operand = SUBREG_REG (operand);
- /* Force reload if this is a constant or PLUS or if there may may
+ /* Force reload if this is a constant or PLUS or if there may
be a problem accessing OPERAND in the outer mode. */
if (CONSTANT_P (operand)
|| GET_CODE (operand) == PLUS
break;
case '?':
- reject += 3;
+ reject += 6;
break;
case '!':
- reject = 300;
+ reject = 600;
break;
case '#':
&& this_alternative_matches[i] < 0)
bad = 1;
- /* Alternative loses if it requires a type of reload not
- permitted for this insn. We can always reload SCRATCH
- and objects with a REG_UNUSED note. */
- if (GET_CODE (operand) != SCRATCH
- && modified[i] != RELOAD_READ && no_output_reloads
- && ! find_reg_note (insn, REG_UNUSED, operand))
- bad = 1;
- else if (modified[i] != RELOAD_WRITE && no_input_reloads)
- bad = 1;
-
/* If this is a constant that is reloaded into the desired
class by copying it to memory first, count that as another
reload. This is consistent with other code and is
if (CONSTANT_P (operand)
/* force_const_mem does not accept HIGH. */
&& GET_CODE (operand) != HIGH
- && (PREFERRED_RELOAD_CLASS (operand,
+ && ((PREFERRED_RELOAD_CLASS (operand,
(enum reg_class) this_alternative[i])
- == NO_REGS)
+ == NO_REGS)
+ || no_input_reloads)
&& operand_mode[i] != VOIDmode)
{
const_to_mem = 1;
== NO_REGS))
bad = 1;
+ /* Alternative loses if it requires a type of reload not
+ permitted for this insn. We can always reload SCRATCH
+ and objects with a REG_UNUSED note. */
+ else if (GET_CODE (operand) != SCRATCH
+ && modified[i] != RELOAD_READ && no_output_reloads
+ && ! find_reg_note (insn, REG_UNUSED, operand))
+ bad = 1;
+ else if (modified[i] != RELOAD_WRITE && no_input_reloads
+ && ! const_to_mem)
+ bad = 1;
+
+
/* We prefer to reload pseudos over reloading other things,
since such reloads may be able to be eliminated later.
If we are reloading a SCRATCH, we won't be generating any
&& REGNO (operand) >= FIRST_PSEUDO_REGISTER)
&& GET_CODE (operand) != SCRATCH
&& ! (const_to_mem && constmemok))
+ reject += 2;
+
+ /* Input reloads can be inherited more often than output
+ reloads can be removed, so penalize output reloads. */
+ if (operand_type[i] != RELOAD_FOR_INPUT
+ && GET_CODE (operand) != SCRATCH)
reject++;
}
this_alternative[i]))
this_alternative[i] = (int) preferred_class[i];
else
- reject += (1 + pref_or_nothing[i]);
+ reject += (2 + 2 * pref_or_nothing[i]);
}
}
}
/* REJECT, set by the ! and ? constraint characters and when a register
would be reloaded into a non-preferred class, discourages the use of
- this alternative for a reload goal. REJECT is incremented by three
- for each ? and one for each non-preferred class. */
- losers = losers * 3 + reject;
+ this alternative for a reload goal. REJECT is incremented by six
+ for each ? and two for each non-preferred class. */
+ losers = losers * 6 + reject;
/* If this alternative can be made to work by reloading,
and it needs less reloading than the others checked so far,
&& CONSTANT_P (recog_operand[i])
/* force_const_mem does not accept HIGH. */
&& GET_CODE (recog_operand[i]) != HIGH
- && (PREFERRED_RELOAD_CLASS (recog_operand[i],
+ && ((PREFERRED_RELOAD_CLASS (recog_operand[i],
(enum reg_class) goal_alternative[i])
- == NO_REGS)
+ == NO_REGS)
+ || no_input_reloads)
&& operand_mode[i] != VOIDmode)
{
*recog_operand_loc[i] = recog_operand[i]
reload_when_needed[reload_secondary_out_reload[secondary_out_reload]]
= RELOAD_FOR_OPADDR_ADDR;
}
- if (reload_when_needed[i] == RELOAD_FOR_INPADDR_ADDRESS
- || reload_when_needed[i] == RELOAD_FOR_OUTADDR_ADDRESS)
- reload_when_needed[i] = RELOAD_FOR_OPADDR_ADDR;
- else
- reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS;
+
+ reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS;
}
if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
reload_in[j] = 0;
}
+ /* Set which reloads must use registers not used in any group. Start
+ with those that conflict with a group and then include ones that
+ conflict with ones that are already known to conflict with a group. */
+
+ changed = 0;
+ for (i = 0; i < n_reloads; i++)
+ {
+ enum machine_mode mode = reload_inmode[i];
+ enum reg_class class = reload_reg_class[i];
+ int size;
+
+ if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode))
+ mode = reload_outmode[i];
+ size = CLASS_MAX_NREGS (class, mode);
+
+ if (size == 1)
+ for (j = 0; j < n_reloads; j++)
+ if ((CLASS_MAX_NREGS (reload_reg_class[j],
+ (GET_MODE_SIZE (reload_outmode[j])
+ > GET_MODE_SIZE (reload_inmode[j]))
+ ? reload_outmode[j] : reload_inmode[j])
+ > 1)
+ && !reload_optional[j]
+ && (reload_in[j] != 0 || reload_out[j] != 0
+ || reload_secondary_p[j])
+ && reloads_conflict (i, j)
+ && reg_classes_intersect_p (class, reload_reg_class[j]))
+ {
+ reload_nongroup[i] = 1;
+ changed = 1;
+ break;
+ }
+ }
+
+ while (changed)
+ {
+ changed = 0;
+
+ for (i = 0; i < n_reloads; i++)
+ {
+ enum machine_mode mode = reload_inmode[i];
+ enum reg_class class = reload_reg_class[i];
+ int size;
+
+ if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode))
+ mode = reload_outmode[i];
+ size = CLASS_MAX_NREGS (class, mode);
+
+ if (! reload_nongroup[i] && size == 1)
+ for (j = 0; j < n_reloads; j++)
+ if (reload_nongroup[j]
+ && reloads_conflict (i, j)
+ && reg_classes_intersect_p (class, reload_reg_class[j]))
+ {
+ reload_nongroup[i] = 1;
+ changed = 1;
+ break;
+ }
+ }
+ }
+
#else /* no REGISTER_CONSTRAINTS */
int noperands;
int insn_code_number;
return 0;
}
+#ifdef LEGITIMIZE_RELOAD_ADDRESS
+ do
+ {
+ if (memrefloc)
+ {
+ LEGITIMIZE_RELOAD_ADDRESS (ad, GET_MODE (*memrefloc), opnum, type,
+ ind_levels, win);
+ }
+ break;
+ win:
+ *memrefloc = copy_rtx (*memrefloc);
+ XEXP (*memrefloc, 0) = ad;
+ move_replacements (&ad, &XEXP (*memrefloc, 0));
+ return 1;
+ }
+ while (0);
+#endif
+
/* The address is not valid. We have to figure out why. One possibility
is that it is itself a MEM. This can happen when the frame pointer is
being eliminated, a pseudo is not allocated to a hard register, and the
}
}
}
+
+/* Change any replacements being done to *X to be done to *Y */
+
+void
+move_replacements (x, y)
+ rtx *x;
+ rtx *y;
+{
+ int i;
+
+ for (i = 0; i < n_replacements; i++)
+ if (replacements[i].subreg_loc == x)
+ replacements[i].subreg_loc = y;
+ else if (replacements[i].where == x)
+ {
+ replacements[i].where = y;
+ replacements[i].subreg_loc = 0;
+ }
+}
\f
/* If LOC was scheduled to be replaced by something, return the replacement.
Otherwise, return *LOC. */
&& XEXP (goal, 0) == stack_pointer_rtx
&& CONSTANT_P (XEXP (goal, 1)))
goal_const = need_stable_sp = 1;
+ else if (GET_CODE (goal) == PLUS
+ && XEXP (goal, 0) == frame_pointer_rtx
+ && CONSTANT_P (XEXP (goal, 1)))
+ goal_const = 1;
else
return 0;
if (reload_optional[r])
fprintf (f, ", optional");
+ if (reload_nongroup[r])
+ fprintf (stderr, ", nongroup");
+
if (reload_inc[r] != 0)
fprintf (f, ", inc by %d", reload_inc[r]);
prefix = "\n\t";
if (reload_secondary_in_icode[r] != CODE_FOR_nothing)
{
- fprintf (f, "%ssecondary_in_icode = %s", prefix, insn_name[r]);
+ fprintf (stderr, "%ssecondary_in_icode = %s", prefix,
+ insn_name[reload_secondary_in_icode[r]]);
prefix = ", ";
}
if (reload_secondary_out_icode[r] != CODE_FOR_nothing)
- fprintf (f, "%ssecondary_out_icode = %s", prefix, insn_name[r]);
+ fprintf (stderr, "%ssecondary_out_icode = %s", prefix,
+ insn_name[reload_secondary_out_icode[r]]);
fprintf (f, "\n");
}