#include "reload.h"
#include "recog.h"
#include "output.h"
+#include "cselib.h"
#include "real.h"
#include "toplev.h"
rtx *reg_equiv_mem;
/* Widest width in which each pseudo reg is referred to (via subreg). */
-static int *reg_max_ref_width;
+static unsigned int *reg_max_ref_width;
/* Element N is the list of insns that initialized reg N from its equivalent
constant or memory slot. */
static rtx spill_stack_slot[FIRST_PSEUDO_REGISTER];
/* Width allocated so far for that stack slot. */
-static int spill_stack_slot_width[FIRST_PSEUDO_REGISTER];
+static unsigned int spill_stack_slot_width[FIRST_PSEUDO_REGISTER];
/* Record which pseudos needed to be spilled. */
static regset_head spilled_pseudos;
static void set_offsets_for_label PARAMS ((rtx));
static void init_elim_table PARAMS ((void));
static void update_eliminables PARAMS ((HARD_REG_SET *));
-static void spill_hard_reg PARAMS ((int, FILE *, int));
+static void spill_hard_reg PARAMS ((unsigned int, FILE *, int));
static int finish_spills PARAMS ((int, FILE *));
static void ior_hard_reg_set PARAMS ((HARD_REG_SET *, HARD_REG_SET *));
static void scan_paradoxical_subregs PARAMS ((rtx));
static void reload_as_needed PARAMS ((int));
static void forget_old_reloads_1 PARAMS ((rtx, rtx, void *));
static int reload_reg_class_lower PARAMS ((const PTR, const PTR));
-static void mark_reload_reg_in_use PARAMS ((int, int, enum reload_type,
- enum machine_mode));
-static void clear_reload_reg_in_use PARAMS ((int, int, enum reload_type,
- enum machine_mode));
-static int reload_reg_free_p PARAMS ((int, int, enum reload_type));
+static void mark_reload_reg_in_use PARAMS ((unsigned int, int,
+ enum reload_type,
+ enum machine_mode));
+static void clear_reload_reg_in_use PARAMS ((unsigned int, int,
+ enum reload_type,
+ enum machine_mode));
+static int reload_reg_free_p PARAMS ((unsigned int, int,
+ enum reload_type));
static int reload_reg_free_for_value_p PARAMS ((int, int, enum reload_type,
- rtx, rtx, int, int));
-static int reload_reg_reaches_end_p PARAMS ((int, int, enum reload_type));
-static int allocate_reload_reg PARAMS ((struct insn_chain *, int, int));
+ rtx, rtx, int, int));
+static int reload_reg_reaches_end_p PARAMS ((unsigned int, int,
+ enum reload_type));
+static int allocate_reload_reg PARAMS ((struct insn_chain *, int,
+ int));
static void failed_reload PARAMS ((rtx, int));
static int set_reload_reg PARAMS ((int, int));
static void choose_reload_regs_init PARAMS ((struct insn_chain *, rtx *));
static void choose_reload_regs PARAMS ((struct insn_chain *));
static void merge_assigned_reloads PARAMS ((rtx));
static void emit_input_reload_insns PARAMS ((struct insn_chain *,
- struct reload *, rtx, int));
+ struct reload *, rtx, int));
static void emit_output_reload_insns PARAMS ((struct insn_chain *,
- struct reload *, int));
+ struct reload *, int));
static void do_input_reload PARAMS ((struct insn_chain *,
- struct reload *, int));
+ struct reload *, int));
static void do_output_reload PARAMS ((struct insn_chain *,
- struct reload *, int));
+ struct reload *, int));
static void emit_reload_insns PARAMS ((struct insn_chain *));
static void delete_output_reload PARAMS ((rtx, int, int));
static void delete_address_reloads PARAMS ((rtx, rtx));
static rtx inc_for_reload PARAMS ((rtx, rtx, rtx, int));
static int constraint_accepts_reg_p PARAMS ((const char *, rtx));
static void reload_cse_regs_1 PARAMS ((rtx));
-static void reload_cse_invalidate_regno PARAMS ((int, enum machine_mode, int));
-static int reload_cse_mem_conflict_p PARAMS ((rtx, rtx));
-static void reload_cse_invalidate_mem PARAMS ((rtx));
-static void reload_cse_invalidate_rtx PARAMS ((rtx, rtx, void *));
-static int reload_cse_regno_equal_p PARAMS ((int, rtx, enum machine_mode));
-static int reload_cse_noop_set_p PARAMS ((rtx, rtx));
+static int reload_cse_noop_set_p PARAMS ((rtx));
static int reload_cse_simplify_set PARAMS ((rtx, rtx));
static int reload_cse_simplify_operands PARAMS ((rtx));
-static void reload_cse_check_clobber PARAMS ((rtx, rtx, void *));
-static void reload_cse_record_set PARAMS ((rtx, rtx));
-static void reload_combine PARAMS ((void));
-static void reload_combine_note_use PARAMS ((rtx *, rtx));
-static void reload_combine_note_store PARAMS ((rtx, rtx, void *));
-static void reload_cse_move2add PARAMS ((rtx));
-static void move2add_note_store PARAMS ((rtx, rtx, void *));
+static void reload_combine PARAMS ((void));
+static void reload_combine_note_use PARAMS ((rtx *, rtx));
+static void reload_combine_note_store PARAMS ((rtx, rtx, void *));
+static void reload_cse_move2add PARAMS ((rtx));
+static void move2add_note_store PARAMS ((rtx, rtx, void *));
#ifdef AUTO_INC_DEC
-static void add_auto_inc_notes PARAMS ((rtx, rtx));
+static void add_auto_inc_notes PARAMS ((rtx, rtx));
#endif
static rtx gen_mode_int PARAMS ((enum machine_mode,
- HOST_WIDE_INT));
+ HOST_WIDE_INT));
static void failed_reload PARAMS ((rtx, int));
static int set_reload_reg PARAMS ((int, int));
+static void reload_cse_delete_noop_set PARAMS ((rtx, rtx));
+static void reload_cse_simplify PARAMS ((rtx));
extern void dump_needs PARAMS ((struct insn_chain *, FILE *));
\f
/* Initialize the reload pass once per compilation. */
/* Small utility function to set all regs in hard reg set TO which are
allocated to pseudos in regset FROM. */
+
void
compute_use_by_pseudos (to, from)
HARD_REG_SET *to;
regset from;
{
- int regno;
+ unsigned int regno;
+
EXECUTE_IF_SET_IN_REG_SET
(from, FIRST_PSEUDO_REGISTER, regno,
{
int r = reg_renumber[regno];
int nregs;
+
if (r < 0)
{
/* reload_combine uses the information from
static int spill_add_cost[FIRST_PSEUDO_REGISTER];
/* Update the spill cost arrays, considering that pseudo REG is live. */
+
static void
count_pseudo (reg)
int reg;
SPILLED_NREGS. Determine how pseudo REG, which is live during the insn,
is affected. We will add it to SPILLED_PSEUDOS if necessary, and we will
update SPILL_COST/SPILL_ADD_COST. */
+
static void
count_spilled_pseudo (spilled, spilled_nregs, reg)
int spilled, spilled_nregs, reg;
struct reload *rl = rld + rnum;
int best_cost = INT_MAX;
int best_reg = -1;
- int i, j;
+ unsigned int i, j;
+ int k;
HARD_REG_SET not_usable;
HARD_REG_SET used_by_other_reload;
IOR_COMPL_HARD_REG_SET (not_usable, reg_class_contents[rl->class]);
CLEAR_HARD_REG_SET (used_by_other_reload);
- for (i = 0; i < order; i++)
+ for (k = 0; k < order; k++)
{
- int other = reload_order[i];
+ int other = reload_order[k];
+
if (rld[other].regno >= 0 && reloads_conflict (other, rnum))
for (j = 0; j < rld[other].nregs; j++)
SET_HARD_REG_BIT (used_by_other_reload, rld[other].regno + j);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
- int regno = i;
+ unsigned int regno = i;
+
if (! TEST_HARD_REG_BIT (not_usable, regno)
&& ! TEST_HARD_REG_BIT (used_by_other_reload, regno)
&& HARD_REGNO_MODE_OK (regno, rl->mode))
{
int this_cost = spill_cost[regno];
int ok = 1;
- int this_nregs = HARD_REGNO_NREGS (regno, rl->mode);
+ unsigned int this_nregs = HARD_REGNO_NREGS (regno, rl->mode);
for (j = 1; j < this_nregs; j++)
{
}
if (best_reg == -1)
return 0;
+
if (dumpfile)
fprintf (dumpfile, "Using reg %d for reload %d\n", best_reg, rnum);
+
rl->nregs = HARD_REGNO_NREGS (best_reg, rl->mode);
rl->regno = best_reg;
{
count_spilled_pseudo (best_reg, rl->nregs, j);
});
+
EXECUTE_IF_SET_IN_REG_SET
(&chain->dead_or_set, FIRST_PSEUDO_REGISTER, j,
{
{
int regno = REGNO (chain->rld[i].reg_rtx);
chain->rld[i].regno = regno;
- chain->rld[i].nregs = HARD_REGNO_NREGS (regno, GET_MODE (chain->rld[i].reg_rtx));
+ chain->rld[i].nregs
+ = HARD_REGNO_NREGS (regno, GET_MODE (chain->rld[i].reg_rtx));
}
else
chain->rld[i].regno = -1;
&& reg_equiv_memory_loc[i] == 0)
{
register rtx x;
- int inherent_size = PSEUDO_REGNO_BYTES (i);
- int total_size = MAX (inherent_size, reg_max_ref_width[i]);
+ unsigned int inherent_size = PSEUDO_REGNO_BYTES (i);
+ unsigned int total_size = MAX (inherent_size, reg_max_ref_width[i]);
int adjust = 0;
/* Each pseudo reg has an inherent size which comes from its own mode,
int regno;
{
register int i, lim;
+
i = reg_renumber[regno];
if (i < 0)
return;
static void
spill_hard_reg (regno, dumpfile, cant_eliminate)
- register int regno;
+ unsigned int regno;
FILE *dumpfile ATTRIBUTE_UNUSED;
int cant_eliminate;
{
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
if (reg_renumber[i] >= 0
- && reg_renumber[i] <= regno
- && (reg_renumber[i]
- + HARD_REGNO_NREGS (reg_renumber[i],
+ && (unsigned int) reg_renumber[i] <= regno
+ && ((unsigned int) reg_renumber[i]
+ + HARD_REGNO_NREGS ((unsigned int) reg_renumber[i],
PSEUDO_REGNO_MODE (i))
> regno))
SET_REGNO_REG_SET (&spilled_pseudos, i);
/* I'm getting weird preprocessor errors if I use IOR_HARD_REG_SET
from within EXECUTE_IF_SET_IN_REG_SET. Hence this awkwardness. */
+
static void
ior_hard_reg_set (set1, set2)
HARD_REG_SET *set1, *set2;
rtx ignored ATTRIBUTE_UNUSED;
void *data ATTRIBUTE_UNUSED;
{
- register int regno;
- int nr;
+ unsigned int regno;
+ unsigned int nr;
int offset = 0;
/* note_stores does give us subregs of hard regs. */
nr = 1;
else
{
- int i;
+ unsigned int i;
+
nr = HARD_REGNO_NREGS (regno, GET_MODE (x));
/* Storing into a spilled-reg invalidates its contents.
This can happen if a block-local pseudo is allocated to that reg
static void
mark_reload_reg_in_use (regno, opnum, type, mode)
- int regno;
+ unsigned int regno;
int opnum;
enum reload_type type;
enum machine_mode mode;
{
- int nregs = HARD_REGNO_NREGS (regno, mode);
- int i;
+ unsigned int nregs = HARD_REGNO_NREGS (regno, mode);
+ unsigned int i;
for (i = regno; i < nregs + regno; i++)
{
static void
clear_reload_reg_in_use (regno, opnum, type, mode)
- int regno;
+ unsigned int regno;
int opnum;
enum reload_type type;
enum machine_mode mode;
{
- int nregs = HARD_REGNO_NREGS (regno, mode);
- int start_regno, end_regno;
+ unsigned int nregs = HARD_REGNO_NREGS (regno, mode);
+ unsigned int start_regno, end_regno, r;
int i;
/* A complication is that for some reload types, inheritance might
allow multiple reloads of the same types to share a reload register.
&& (check_any || rld[i].opnum == opnum)
&& rld[i].reg_rtx)
{
- int conflict_start = true_regnum (rld[i].reg_rtx);
- int conflict_end
+ unsigned int conflict_start = true_regnum (rld[i].reg_rtx);
+ unsigned int conflict_end
= (conflict_start
+ HARD_REGNO_NREGS (conflict_start, rld[i].mode));
}
}
}
- for (i = start_regno; i < end_regno; i++)
- CLEAR_HARD_REG_BIT (*used_in_set, i);
+
+ for (r = start_regno; r < end_regno; r++)
+ CLEAR_HARD_REG_BIT (*used_in_set, r);
}
/* 1 if reg REGNO is free as a reload reg for a reload of the sort
static int
reload_reg_free_p (regno, opnum, type)
- int regno;
+ unsigned int regno;
int opnum;
enum reload_type type;
{
static int
reload_reg_reaches_end_p (regno, opnum, type)
- int regno;
+ unsigned int regno;
int opnum;
enum reload_type type;
{
{
rtx insn = chain->insn;
register int i, j;
- int max_group_size = 1;
+ unsigned int max_group_size = 1;
enum reg_class group_class = NO_REGS;
int pass, win, inheritance;
if (rld[j].nregs > 1)
{
max_group_size = MAX (rld[j].nregs, max_group_size);
- group_class = reg_class_superunion[(int)rld[j].class][(int)group_class];
+ group_class
+ = reg_class_superunion[(int)rld[j].class][(int)group_class];
}
save_reload_reg_rtx[j] = rld[j].reg_rtx;
/* Process the reloads in order of preference just found.
Beyond this point, subregs can be found in reload_reg_rtx.
- This used to look for an existing reloaded home for all
- of the reloads, and only then perform any new reloads.
- But that could lose if the reloads were done out of reg-class order
- because a later reload with a looser constraint might have an old
- home in a register needed by an earlier reload with a tighter constraint.
+ This used to look for an existing reloaded home for all of the
+ reloads, and only then perform any new reloads. But that could lose
+ if the reloads were done out of reg-class order because a later
+ reload with a looser constraint might have an old home in a register
+ needed by an earlier reload with a tighter constraint.
To solve this, we make two passes over the reloads, in the order
described above. In the first pass we try to inherit a reload
/* 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. */
+
static void
emit_input_reload_insns (chain, rl, old, j)
struct insn_chain *chain;
if (oldequiv)
{
- int regno = true_regnum (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. */
return count;
}
\f
-/* This array holds values which are equivalent to a hard register
- during reload_cse_regs. Each array element is an EXPR_LIST of
- values. Each time a hard register is set, we set the corresponding
- array element to the value. Each time a hard register is copied
- into memory, we add the memory location to the corresponding array
- element. We don't store values or memory addresses with side
- effects in this array.
-
- If the value is a CONST_INT, then the mode of the containing
- EXPR_LIST is the mode in which that CONST_INT was referenced.
-
- We sometimes clobber a specific entry in a list. In that case, we
- just set XEXP (list-entry, 0) to 0. */
-
-static rtx *reg_values;
-
-/* This is a preallocated REG rtx which we use as a temporary in
- reload_cse_invalidate_regno, so that we don't need to allocate a
- new one each time through a loop in that function. */
-
-static rtx invalidate_regno_rtx;
-
-/* Invalidate any entries in reg_values which depend on REGNO,
- including those for REGNO itself. This is called if REGNO is
- changing. If CLOBBER is true, then always forget anything we
- currently know about REGNO. MODE is the mode of the assignment to
- REGNO, which is used to determine how many hard registers are being
- changed. If MODE is VOIDmode, then only REGNO is being changed;
- this is used when invalidating call clobbered registers across a
- call. */
-
+/* INSN is a no-op; delete it.
+ If this sets the return value of the function, we must keep a USE around,
+ in case this is in a different basic block than the final USE. Otherwise,
+ we could loose important register lifeness information on
+ SMALL_REGISTER_CLASSES machines, where return registers might be used as
+ spills: subsequent passes assume that spill registers are dead at the end
+ of a basic block.
+ VALUE must be the return value in such a case, NULL otherwise. */
static void
-reload_cse_invalidate_regno (regno, mode, clobber)
- int regno;
- enum machine_mode mode;
- int clobber;
+reload_cse_delete_noop_set (insn, value)
+ rtx insn, value;
{
- int endregno;
- register int i;
-
- /* Our callers don't always go through true_regnum; we may see a
- pseudo-register here from a CLOBBER or the like. We probably
- won't ever see a pseudo-register that has a real register number,
- for we check anyhow for safety. */
- if (regno >= FIRST_PSEUDO_REGISTER)
- regno = reg_renumber[regno];
- if (regno < 0)
- return;
-
- if (mode == VOIDmode)
- endregno = regno + 1;
- else
- endregno = regno + HARD_REGNO_NREGS (regno, mode);
-
- if (clobber)
- for (i = regno; i < endregno; i++)
- reg_values[i] = 0;
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (value)
{
- rtx x;
-
- for (x = reg_values[i]; x; x = XEXP (x, 1))
- {
- if (XEXP (x, 0) != 0
- && refers_to_regno_p (regno, endregno, XEXP (x, 0), NULL_PTR))
- {
- /* If this is the only entry on the list, clear
- reg_values[i]. Otherwise, just clear this entry on
- the list. */
- if (XEXP (x, 1) == 0 && x == reg_values[i])
- {
- reg_values[i] = 0;
- break;
- }
- XEXP (x, 0) = 0;
- }
- }
+ PATTERN (insn) = gen_rtx_USE (VOIDmode, value);
+ INSN_CODE (insn) = -1;
+ REG_NOTES (insn) = NULL_RTX;
}
-
- /* 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++)
+ else
{
- 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;
- }
- }
- }
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
}
}
-/* The memory at address MEM_BASE is being changed.
- Return whether this change will invalidate VAL. */
-
+/* See whether a single set SET is a noop. */
static int
-reload_cse_mem_conflict_p (mem_base, val)
- rtx mem_base;
- rtx val;
+reload_cse_noop_set_p (set)
+ rtx set;
{
- enum rtx_code code;
- const char *fmt;
- int i;
-
- code = GET_CODE (val);
- switch (code)
- {
- /* Get rid of a few simple cases quickly. */
- case REG:
- case PC:
- case CC0:
- case SCRATCH:
- case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case LABEL_REF:
- return 0;
-
- case MEM:
- 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;
- }
+ return rtx_equal_for_cselib_p (SET_DEST (set), SET_SRC (set));
+}
- fmt = GET_RTX_FORMAT (code);
+/* Try to simplify INSN. */
+static void
+reload_cse_simplify (insn)
+ rtx insn;
+{
+ rtx body = PATTERN (insn);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (GET_CODE (body) == SET)
{
- if (fmt[i] == 'e')
+ int count = 0;
+ if (reload_cse_noop_set_p (body))
{
- 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, XVECEXP (val, i, j)))
- return 1;
+ rtx value = SET_DEST (body);
+ if (! REG_FUNCTION_VALUE_P (SET_DEST (body)))
+ value = 0;
+ reload_cse_delete_noop_set (insn, value);
+ return;
}
- }
-
- return 0;
-}
-
-/* Invalidate any entries in reg_values which are changed because of a
- store to MEM_RTX. If this is called because of a non-const call
- instruction, MEM_RTX is (mem:BLK const0_rtx). */
-static void
-reload_cse_invalidate_mem (mem_rtx)
- rtx mem_rtx;
-{
- register int i;
+ /* It's not a no-op, but we can try to simplify it. */
+ count += reload_cse_simplify_set (body, insn);
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (count > 0)
+ apply_change_group ();
+ else
+ reload_cse_simplify_operands (insn);
+ }
+ else if (GET_CODE (body) == PARALLEL)
{
- rtx x;
+ int i;
+ int count = 0;
+ rtx value = NULL_RTX;
- for (x = reg_values[i]; x; x = XEXP (x, 1))
+ /* 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 (XEXP (x, 0) != 0
- && reload_cse_mem_conflict_p (mem_rtx, XEXP (x, 0)))
+ rtx part = XVECEXP (body, 0, i);
+ if (GET_CODE (part) == SET)
{
- /* If this is the only entry on the list, clear
- reg_values[i]. Otherwise, just clear this entry on
- the list. */
- if (XEXP (x, 1) == 0 && x == reg_values[i])
+ if (! reload_cse_noop_set_p (part))
+ break;
+ if (REG_FUNCTION_VALUE_P (SET_DEST (part)))
{
- reg_values[i] = 0;
- break;
+ if (value)
+ break;
+ value = SET_DEST (part);
}
- XEXP (x, 0) = 0;
}
+ else if (GET_CODE (part) != CLOBBER)
+ break;
}
- }
-}
-/* Invalidate DEST, which is being assigned to or clobbered. The
- second parameter exists so that this function can be passed to
- note_stores; it is ignored. */
+ if (i < 0)
+ {
+ reload_cse_delete_noop_set (insn, value);
+ /* We're done with this insn. */
+ return;
+ }
-static void
-reload_cse_invalidate_rtx (dest, ignore, data)
- rtx dest;
- rtx ignore ATTRIBUTE_UNUSED;
- void *data ATTRIBUTE_UNUSED;
-{
- while (GET_CODE (dest) == STRICT_LOW_PART
- || GET_CODE (dest) == SIGN_EXTRACT
- || GET_CODE (dest) == ZERO_EXTRACT
- || GET_CODE (dest) == SUBREG)
- dest = XEXP (dest, 0);
-
- if (GET_CODE (dest) == REG)
- reload_cse_invalidate_regno (REGNO (dest), GET_MODE (dest), 1);
- else if (GET_CODE (dest) == MEM)
- reload_cse_invalidate_mem (dest);
+ /* It's not a no-op, but we can try to simplify it. */
+ 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 ();
+ else
+ reload_cse_simplify_operands (insn);
+ }
}
/* Do a very simple CSE pass over the hard registers.
reload_cse_regs_1 (first)
rtx first;
{
- char *firstobj;
- rtx callmem;
- register int i;
rtx insn;
+ cselib_init ();
init_alias_analysis ();
- reg_values = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
- bzero ((char *)reg_values, FIRST_PSEUDO_REGISTER * sizeof (rtx));
-
- /* Create our EXPR_LIST structures on reload_obstack, so that we can
- free them when we are done. */
- push_obstacks (&reload_obstack, &reload_obstack);
- firstobj = (char *) obstack_alloc (&reload_obstack, 0);
-
- /* We pass this to reload_cse_invalidate_mem to invalidate all of
- memory for a non-const call instruction. */
- callmem = gen_rtx_MEM (BLKmode, const0_rtx);
-
- /* This is used in reload_cse_invalidate_regno to avoid consing a
- new REG in a loop in that function. */
- invalidate_regno_rtx = gen_rtx_REG (VOIDmode, 0);
-
for (insn = first; insn; insn = NEXT_INSN (insn))
{
- rtx body;
-
- if (GET_CODE (insn) == CODE_LABEL)
- {
- /* Forget all the register values at a code label. We don't
- try to do anything clever around jumps. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- reg_values[i] = 0;
-
- continue;
- }
-
-#ifdef NON_SAVING_SETJMP
- if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- {
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- reg_values[i] = 0;
-
- continue;
- }
-#endif
-
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
-
- /* If this is a call instruction, forget anything stored in a
- call clobbered register, or, if this is not a const call, in
- memory. */
- if (GET_CODE (insn) == CALL_INSN)
- {
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i])
- reload_cse_invalidate_regno (i, VOIDmode, 1);
-
- if (! CONST_CALL_P (insn))
- reload_cse_invalidate_mem (callmem);
- }
-
-
- /* Forget all the register values at a volatile asm. */
- if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
- && MEM_VOLATILE_P (PATTERN (insn)))
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- reg_values[i] = 0;
-
- body = PATTERN (insn);
- if (GET_CODE (body) == SET)
- {
- int count = 0;
- if (reload_cse_noop_set_p (body, insn))
- {
- /* If this sets the return value of the function, we must keep
- a USE around, in case this is in a different basic block
- than the final USE. Otherwise, we could loose important
- register lifeness information on SMALL_REGISTER_CLASSES
- machines, where return registers might be used as spills:
- subsequent passes assume that spill registers are dead at
- the end of a basic block. */
- if (REG_FUNCTION_VALUE_P (SET_DEST (body)))
- {
- pop_obstacks ();
- PATTERN (insn) = gen_rtx_USE (VOIDmode, SET_DEST (body));
- INSN_CODE (insn) = -1;
- REG_NOTES (insn) = NULL_RTX;
- push_obstacks (&reload_obstack, &reload_obstack);
- }
- else
- {
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
-
- /* We're done with this insn. */
- continue;
- }
-
- /* It's not a no-op, but we can try to simplify it. */
- count += reload_cse_simplify_set (body, insn);
-
- if (count > 0)
- apply_change_group ();
- else
- reload_cse_simplify_operands (insn);
-
- reload_cse_record_set (body, body);
- }
- else if (GET_CODE (body) == PARALLEL)
- {
- int count = 0;
- rtx value = NULL_RTX;
-
- /* If every action in a PARALLEL is a noop, we can delete
- the entire PARALLEL. */
- for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
- {
- rtx part = XVECEXP (body, 0, i);
- if (GET_CODE (part) == SET)
- {
- if (! reload_cse_noop_set_p (part, insn))
- break;
- if (REG_FUNCTION_VALUE_P (SET_DEST (part)))
- {
- if (value)
- break;
- value = SET_DEST (part);
- }
- }
- else if (GET_CODE (part) != CLOBBER)
- break;
- }
- if (i < 0)
- {
- if (value)
- {
- pop_obstacks ();
- PATTERN (insn) = gen_rtx_USE (VOIDmode, value);
- INSN_CODE (insn) = -1;
- REG_NOTES (insn) = NULL_RTX;
- push_obstacks (&reload_obstack, &reload_obstack);
- }
- else
- {
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
-
- /* We're done with this insn. */
- continue;
- }
-
- /* It's not a no-op, but we can try to simplify it. */
- 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 ();
- else
- reload_cse_simplify_operands (insn);
-
- /* Look through the PARALLEL and record the values being
- set, if possible. Also handle any CLOBBERs. */
- for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
- {
- rtx x = XVECEXP (body, 0, i);
-
- if (GET_CODE (x) == SET)
- reload_cse_record_set (x, body);
- else
- note_stores (x, reload_cse_invalidate_rtx, NULL);
- }
- }
- else
- note_stores (body, reload_cse_invalidate_rtx, NULL);
-
-#ifdef AUTO_INC_DEC
- /* Clobber any registers which appear in REG_INC notes. We
- could keep track of the changes to their values, but it is
- unlikely to help. */
- {
- rtx x;
-
- for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
- if (REG_NOTE_KIND (x) == REG_INC)
- reload_cse_invalidate_rtx (XEXP (x, 0), NULL_RTX, NULL);
- }
-#endif
-
- /* Look for any CLOBBERs in CALL_INSN_FUNCTION_USAGE, but only
- after we have processed the insn. */
- if (GET_CODE (insn) == CALL_INSN)
- {
- rtx x;
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ reload_cse_simplify (insn);
- for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
- if (GET_CODE (XEXP (x, 0)) == CLOBBER)
- reload_cse_invalidate_rtx (XEXP (XEXP (x, 0), 0), NULL_RTX,
- NULL);
- }
+ cselib_process_insn (insn);
}
/* Clean up. */
end_alias_analysis ();
-
- /* Free all the temporary structures we created, and go back to the
- regular obstacks. */
- obstack_free (&reload_obstack, firstobj);
- pop_obstacks ();
+ cselib_finish ();
}
/* Call cse / combine like post-reload optimization phases.
reload_cse_regs_1 (first);
}
-/* Return whether the values known for REGNO are equal to VAL. MODE
- is the mode of the object that VAL is being copied to; this matters
- if VAL is a CONST_INT. */
-
-static int
-reload_cse_regno_equal_p (regno, val, mode)
- int regno;
- rtx val;
- enum machine_mode mode;
-{
- rtx x;
-
- if (val == 0)
- return 0;
-
- for (x = reg_values[regno]; x; x = XEXP (x, 1))
- if (XEXP (x, 0) != 0
- && rtx_equal_p (XEXP (x, 0), val)
- && (! flag_float_store || GET_CODE (XEXP (x, 0)) != MEM
- || GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT)
- && (GET_CODE (val) != CONST_INT
- || mode == GET_MODE (x)
- || (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x))
- /* On a big endian machine if the value spans more than
- one register then this register holds the high part of
- it and we can't use it.
-
- ??? We should also compare with the high part of the
- value. */
- && !(WORDS_BIG_ENDIAN
- && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
- && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
- GET_MODE_BITSIZE (GET_MODE (x))))))
- return 1;
-
- return 0;
-}
-
-/* See whether a single set is a noop. SET is the set instruction we
- are should check, and INSN is the instruction from which it came. */
-
-static int
-reload_cse_noop_set_p (set, insn)
- rtx set;
- rtx insn ATTRIBUTE_UNUSED;
-{
- rtx src, dest;
- enum machine_mode dest_mode;
- int dreg, sreg;
- int ret;
-
- src = SET_SRC (set);
- dest = SET_DEST (set);
- dest_mode = GET_MODE (dest);
-
- if (side_effects_p (src))
- return 0;
-
- dreg = true_regnum (dest);
- sreg = true_regnum (src);
-
- /* Check for setting a register to itself. In this case, we don't
- have to worry about REG_DEAD notes. */
- if (dreg >= 0 && dreg == sreg)
- return 1;
-
- ret = 0;
- if (dreg >= 0)
- {
- /* Check for setting a register to itself. */
- if (dreg == sreg)
- ret = 1;
-
- /* Check for setting a register to a value which we already know
- is in the register. */
- else if (reload_cse_regno_equal_p (dreg, src, dest_mode))
- ret = 1;
-
- /* Check for setting a register DREG to another register SREG
- where SREG is equal to a value which is already in DREG. */
- else if (sreg >= 0)
- {
- rtx x;
-
- for (x = reg_values[sreg]; x; x = XEXP (x, 1))
- {
- rtx tmp;
-
- if (XEXP (x, 0) == 0)
- continue;
-
- if (dest_mode == GET_MODE (x))
- tmp = XEXP (x, 0);
- else if (GET_MODE_BITSIZE (dest_mode)
- < GET_MODE_BITSIZE (GET_MODE (x)))
- tmp = gen_lowpart_common (dest_mode, XEXP (x, 0));
- else
- continue;
-
- if (tmp
- && reload_cse_regno_equal_p (dreg, tmp, dest_mode))
- {
- ret = 1;
- break;
- }
- }
- }
- }
- else if (GET_CODE (dest) == MEM)
- {
- /* Check for storing a register to memory when we know that the
- register is equivalent to the memory location. */
- if (sreg >= 0
- && reload_cse_regno_equal_p (sreg, dest, dest_mode)
- && ! side_effects_p (dest))
- ret = 1;
- }
-
- return ret;
-}
-
/* Try to simplify a single SET instruction. SET is the set pattern.
INSN is the instruction it came from.
This function only handles one case: if we set a register to a value
rtx set;
rtx insn;
{
+ int did_change = 0;
int dreg;
rtx src;
- enum machine_mode dest_mode;
enum reg_class dclass;
- register int i;
+ int old_cost;
+ cselib_val *val;
+ struct elt_loc_list *l;
dreg = true_regnum (SET_DEST (set));
if (dreg < 0)
dclass = REGNO_REG_CLASS (dreg);
/* If memory loads are cheaper than register copies, don't change them. */
- if (GET_CODE (src) == MEM
- && MEMORY_MOVE_COST (GET_MODE (src), dclass, 1) < 2)
- return 0;
+ if (GET_CODE (src) == MEM)
+ old_cost = MEMORY_MOVE_COST (GET_MODE (src), dclass, 1);
+ else if (CONSTANT_P (src))
+ old_cost = rtx_cost (src, SET);
+ else if (GET_CODE (src) == REG)
+ old_cost = REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (src)), dclass);
+ else
+ /* ??? */
+ old_cost = rtx_cost (src, SET);
- /* If the constant is cheaper than a register, don't change it. */
- if (CONSTANT_P (src)
- && rtx_cost (src, SET) < 2)
+ val = cselib_lookup (src, VOIDmode, 0);
+ if (! val)
return 0;
-
- dest_mode = GET_MODE (SET_DEST (set));
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- {
- if (i != dreg
- && REGISTER_MOVE_COST (REGNO_REG_CLASS (i), dclass) == 2
- && reload_cse_regno_equal_p (i, src, dest_mode))
- {
- int validated;
-
- /* Pop back to the real obstacks while changing the insn. */
- pop_obstacks ();
-
- validated = validate_change (insn, &SET_SRC (set),
- gen_rtx_REG (dest_mode, i), 1);
-
- /* Go back to the obstack we are using for temporary
- storage. */
- push_obstacks (&reload_obstack, &reload_obstack);
-
- if (validated)
- return 1;
- }
+ for (l = val->locs; l; l = l->next)
+ {
+ int this_cost;
+ if (CONSTANT_P (l->loc) && ! references_value_p (l->loc, 0))
+ this_cost = rtx_cost (l->loc, SET);
+ else if (GET_CODE (l->loc) == REG)
+ this_cost = REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (l->loc)),
+ dclass);
+ else
+ continue;
+ /* If equal costs, prefer registers over anything else. That tends to
+ lead to smaller instructions on some machines. */
+ if ((this_cost < old_cost
+ || (this_cost == old_cost
+ && GET_CODE (l->loc) == REG
+ && GET_CODE (SET_SRC (set)) != REG))
+ && validate_change (insn, &SET_SRC (set), copy_rtx (l->loc), 1))
+ old_cost = this_cost, did_change = 1;
}
- return 0;
+
+ return did_change;
}
/* Try to replace operands in INSN with equivalent values that are already
{
int i,j;
+ /* For each operand, all registers that are equivalent to it. */
+ HARD_REG_SET equiv_regs[MAX_RECOG_OPERANDS];
+
const char *constraints[MAX_RECOG_OPERANDS];
/* Vector recording how bad an alternative is. */
/* Figure out which alternative currently matches. */
if (! constrain_operands (1))
fatal_insn_not_found (insn);
-
+
alternative_reject = (int *) alloca (recog_data.n_alternatives * sizeof (int));
alternative_nregs = (int *) alloca (recog_data.n_alternatives * sizeof (int));
alternative_order = (int *) alloca (recog_data.n_alternatives * sizeof (int));
bzero ((char *)alternative_reject, recog_data.n_alternatives * sizeof (int));
bzero ((char *)alternative_nregs, recog_data.n_alternatives * sizeof (int));
+ /* For each operand, find out which regs are equivalent. */
+ for (i = 0; i < recog_data.n_operands; i++)
+ {
+ cselib_val *v;
+ struct elt_loc_list *l;
+
+ CLEAR_HARD_REG_SET (equiv_regs[i]);
+
+ /* cselib blows up on CODE_LABELs. Trying to fix that doesn't seem
+ right, so avoid the problem here. */
+ if (GET_CODE (recog_data.operand[i]) == CODE_LABEL)
+ continue;
+
+ v = cselib_lookup (recog_data.operand[i], recog_data.operand_mode[i], 0);
+ if (! v)
+ continue;
+
+ for (l = v->locs; l; l = l->next)
+ if (GET_CODE (l->loc) == REG)
+ SET_HARD_REG_BIT (equiv_regs[i], REGNO (l->loc));
+ }
+
for (i = 0; i < recog_data.n_operands; i++)
{
enum machine_mode mode;
{
int class = (int) NO_REGS;
- if (! reload_cse_regno_equal_p (regno, recog_data.operand[i], mode))
+ if (! TEST_HARD_REG_BIT (equiv_regs[i], regno))
continue;
REGNO (reg) = regno;
alternative. */
j = alternative_order[0];
- /* Pop back to the real obstacks while changing the insn. */
- pop_obstacks ();
-
for (i = 0; i < recog_data.n_operands; i++)
{
enum machine_mode mode = recog_data.operand_mode[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 ();
}
-
-/* These two variables are used to pass information from
- reload_cse_record_set to reload_cse_check_clobber. */
-
-static int reload_cse_check_clobbered;
-static rtx reload_cse_check_src;
-
-/* See if DEST overlaps with RELOAD_CSE_CHECK_SRC. If it does, set
- RELOAD_CSE_CHECK_CLOBBERED. This is called via note_stores. The
- second argument, which is passed by note_stores, is ignored. */
-
-static void
-reload_cse_check_clobber (dest, ignore, data)
- rtx dest;
- rtx ignore ATTRIBUTE_UNUSED;
- void *data ATTRIBUTE_UNUSED;
-{
- if (reg_overlap_mentioned_p (dest, reload_cse_check_src))
- reload_cse_check_clobbered = 1;
-}
-
-/* Record the result of a SET instruction. SET is the set pattern.
- BODY is the pattern of the insn that it came from. */
-
-static void
-reload_cse_record_set (set, body)
- rtx set;
- rtx body;
-{
- rtx dest, src, x;
- int dreg, sreg;
- enum machine_mode dest_mode;
-
- dest = SET_DEST (set);
- src = SET_SRC (set);
- dreg = true_regnum (dest);
- sreg = true_regnum (src);
- dest_mode = GET_MODE (dest);
-
- /* Some machines don't define AUTO_INC_DEC, but they still use push
- instructions. We need to catch that case here in order to
- invalidate the stack pointer correctly. Note that invalidating
- the stack pointer is different from invalidating DEST. */
- x = dest;
- while (GET_CODE (x) == SUBREG
- || GET_CODE (x) == ZERO_EXTRACT
- || GET_CODE (x) == SIGN_EXTRACT
- || GET_CODE (x) == STRICT_LOW_PART)
- x = XEXP (x, 0);
- if (push_operand (x, GET_MODE (x)))
- {
- reload_cse_invalidate_rtx (stack_pointer_rtx, NULL_RTX, NULL);
- reload_cse_invalidate_rtx (dest, NULL_RTX, NULL);
- return;
- }
-
- /* We can only handle an assignment to a register, or a store of a
- register to a memory location. For other cases, we just clobber
- the destination. We also have to just clobber if there are side
- effects in SRC or DEST. */
- if ((dreg < 0 && GET_CODE (dest) != MEM)
- || side_effects_p (src)
- || side_effects_p (dest))
- {
- reload_cse_invalidate_rtx (dest, NULL_RTX, NULL);
- return;
- }
-
-#ifdef HAVE_cc0
- /* We don't try to handle values involving CC, because it's a pain
- to keep track of when they have to be invalidated. */
- if (reg_mentioned_p (cc0_rtx, src)
- || reg_mentioned_p (cc0_rtx, dest))
- {
- reload_cse_invalidate_rtx (dest, NULL_RTX, NULL);
- return;
- }
-#endif
-
- /* If BODY is a PARALLEL, then we need to see whether the source of
- SET is clobbered by some other instruction in the PARALLEL. */
- if (GET_CODE (body) == PARALLEL)
- {
- int i;
-
- for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
- {
- rtx x;
-
- x = XVECEXP (body, 0, i);
- if (x == set)
- continue;
-
- reload_cse_check_clobbered = 0;
- reload_cse_check_src = src;
- note_stores (x, reload_cse_check_clobber, NULL);
- if (reload_cse_check_clobbered)
- {
- reload_cse_invalidate_rtx (dest, NULL_RTX, NULL);
- return;
- }
- }
- }
-
- if (dreg >= 0)
- {
- int i;
-
- /* This is an assignment to a register. Update the value we
- have stored for the register. */
- if (sreg >= 0)
- {
- rtx x;
-
- /* This is a copy from one register to another. Any values
- which were valid for SREG are now valid for DREG. If the
- mode changes, we use gen_lowpart_common to extract only
- the part of the value that is copied. */
- reg_values[dreg] = 0;
- for (x = reg_values[sreg]; x; x = XEXP (x, 1))
- {
- rtx tmp;
-
- if (XEXP (x, 0) == 0)
- continue;
- if (dest_mode == GET_MODE (XEXP (x, 0)))
- tmp = XEXP (x, 0);
- else if (GET_MODE_BITSIZE (dest_mode)
- > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))
- continue;
- else
- tmp = gen_lowpart_common (dest_mode, XEXP (x, 0));
- if (tmp)
- reg_values[dreg] = gen_rtx_EXPR_LIST (dest_mode, tmp,
- reg_values[dreg]);
- }
- }
- else
- reg_values[dreg] = gen_rtx_EXPR_LIST (dest_mode, src, NULL_RTX);
-
- /* We've changed DREG, so invalidate any values held by other
- registers that depend upon it. */
- reload_cse_invalidate_regno (dreg, dest_mode, 0);
-
- /* If this assignment changes more than one hard register,
- forget anything we know about the others. */
- for (i = 1; i < HARD_REGNO_NREGS (dreg, dest_mode); i++)
- reg_values[dreg + i] = 0;
- }
- else if (GET_CODE (dest) == MEM)
- {
- /* Invalidate conflicting memory locations. */
- reload_cse_invalidate_mem (dest);
-
- /* If we're storing a register to memory, add DEST to the list
- in REG_VALUES. */
- if (sreg >= 0 && ! side_effects_p (dest))
- reg_values[sreg] = gen_rtx_EXPR_LIST (dest_mode, dest,
- reg_values[sreg]);
- }
- else
- {
- /* We should have bailed out earlier. */
- abort ();
- }
-}
\f
/* If reload couldn't use reg+reg+offset addressing, try to use reg+reg
addressing now.
reg_offset[n] / reg_base_reg[n] / reg_mode[n] are only valid if
reg_set_luid[n] is larger than last_label_luid[n] . */
static int reg_set_luid[FIRST_PSEUDO_REGISTER];
+
/* reg_offset[n] has to be CONST_INT for it and reg_base_reg[n] /
reg_mode[n] to be valid.
If reg_offset[n] is a CONST_INT and reg_base_reg[n] is negative, register n
static rtx reg_offset[FIRST_PSEUDO_REGISTER];
static int reg_base_reg[FIRST_PSEUDO_REGISTER];
static enum machine_mode reg_mode[FIRST_PSEUDO_REGISTER];
+
/* move2add_luid is linearily increased while scanning the instructions
from first to last. It is used to set reg_set_luid in
reload_cse_move2add and move2add_note_store. */
static int move2add_luid;
/* Generate a CONST_INT and force it in the range of MODE. */
+
static rtx
gen_mode_int (mode, value)
enum machine_mode mode;
...
(set (REGX) (plus (REGX) (CONST_INT B-A))) */
else if (GET_CODE (src) == REG
- && reg_base_reg[regno] == REGNO (src)
+ && reg_base_reg[regno] == (int) REGNO (src)
&& reg_set_luid[regno] > reg_set_luid[REGNO (src)])
{
rtx next = next_nonnote_insn (insn);
/* SET is a SET or CLOBBER that sets DST.
Update reg_set_luid, reg_offset and reg_base_reg accordingly.
Called from reload_cse_move2add via note_stores. */
+
static void
move2add_note_store (dst, set, data)
rtx dst, set;
void *data ATTRIBUTE_UNUSED;
{
- int regno = 0;
- int i;
-
+ unsigned int regno = 0;
+ unsigned int i;
enum machine_mode mode = GET_MODE (dst);
+
if (GET_CODE (dst) == SUBREG)
{
regno = SUBREG_WORD (dst);
dst = SUBREG_REG (dst);
}
+
if (GET_CODE (dst) != REG)
return;
case PLUS:
{
rtx src0 = XEXP (src, 0);
+
if (GET_CODE (src0) == REG)
{
if (REGNO (src0) != regno
reg_base_reg[regno] = REGNO (src0);
reg_set_luid[regno] = move2add_luid;
}
+
reg_offset[regno] = XEXP (src, 1);
break;
}
+
reg_set_luid[regno] = move2add_luid;
reg_offset[regno] = set; /* Invalidate contents. */
break;
}
else
{
- for (i = regno + HARD_REGNO_NREGS (regno, mode) - 1; i >= regno; i--)
+ unsigned int endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+ for (i = regno; i < endregno; i++)
{
/* Indicate that this register has been recently written to,
but the exact contents are not available. */