/* Allocate registers within a basic block, for GNU compiler.
Copyright (C) 1987, 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "hard-reg-set.h"
#include "rtl.h"
#include "tm_p.h"
#include "flags.h"
-#include "hard-reg-set.h"
#include "basic-block.h"
#include "regs.h"
#include "function.h"
#include "output.h"
#include "toplev.h"
#include "except.h"
+#include "integrate.h"
\f
/* Next quantity number available for allocation. */
static int next_qty;
-/* Information we maitain about each quantity. */
+/* Information we maintain about each quantity. */
struct qty
{
/* The number of refs to quantity Q. */
or -1 if none was found. */
short phys_reg;
-
- /* Nonzero if this quantity has been used in a SUBREG in some
- way that is illegal. */
-
- char changes_mode;
-
};
static struct qty *qty;
struct equivalence
{
/* Set when an attempt should be made to replace a register
- with the associated src entry. */
+ with the associated src_p entry. */
char replace;
rtx replacement;
- rtx src;
+ rtx *src_p;
/* Loop depth is used to recognize equivalences which appear
to be present within the same loop (or in an inner loop). */
enum machine_mode mode;
int size, birth;
{
- register int qtyno = next_qty++;
+ int qtyno = next_qty++;
reg_qty[regno] = qtyno;
reg_offset[regno] = 0;
qty[qtyno].alternate_class = reg_alternate_class (regno);
qty[qtyno].n_refs = REG_N_REFS (regno);
qty[qtyno].freq = REG_FREQ (regno);
- qty[qtyno].changes_mode = REG_CHANGES_MODE (regno);
}
\f
/* Main entry point of this file. */
int
local_alloc ()
{
- register int b, i;
+ int i;
int max_qty;
+ basic_block b;
/* We need to keep track of whether or not we recorded a LABEL_REF so
that we know if the jump optimizer needs to be rerun. */
/* Promote REG_EQUAL notes to REG_EQUIV notes and adjust status of affected
registers. */
- update_equiv_regs ();
+ if (optimize)
+ update_equiv_regs ();
/* This sets the maximum number of quantities we can have. Quantity
numbers start at zero and we can have one for each pseudo. */
/* Allocate each block's local registers, block by block. */
- for (b = 0; b < n_basic_blocks; b++)
+ FOR_EACH_BB (b)
{
/* NEXT_QTY indicates which elements of the `qty_...'
vectors might need to be initialized because they were used
next_qty = 0;
- block_alloc (b);
+ block_alloc (b->index);
}
free (qty);
equiv_init_varies_p (x)
rtx x;
{
- register RTX_CODE code = GET_CODE (x);
- register int i;
- register const char *fmt;
+ RTX_CODE code = GET_CODE (x);
+ int i;
+ const char *fmt;
switch (code)
{
case CONST:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
return 0;
return 0;
}
-/* Returns non-zero if X (used to initialize register REGNO) is movable.
+/* Returns nonzero if X (used to initialize register REGNO) is movable.
X is only movable if the registers it uses have equivalent initializations
which appear to be within the same loop (or in an inner loop) and movable
or if they are not candidates for local_alloc and don't vary. */
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case PC:
case CC0:
case HIGH:
- case LO_SUM:
return 0;
case REG:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case PC:
case CC0:
case HIGH:
}
\f
/* Return nonzero if the rtx X is invariant over the current function. */
+/* ??? Actually, the places this is used in reload expect exactly what
+ is tested here, and not everything that is function invariant. In
+ particular, the frame pointer and arg pointer are special cased;
+ pic_offset_table_rtx is not, and this will cause aborts when we
+ go to spill these things to memory. */
+
int
function_invariant_p (x)
rtx x;
update_equiv_regs ()
{
rtx insn;
- int block;
+ basic_block bb;
int loop_depth;
regset_head cleared_regs;
int clear_regnos = 0;
/* Scan the insns and find which registers have equivalences. Do this
in a separate scan of the insns because (due to -fcse-follow-jumps)
a register can be set below its use. */
- for (block = 0; block < n_basic_blocks; block++)
+ FOR_EACH_BB (bb)
{
- basic_block bb = BASIC_BLOCK (block);
loop_depth = bb->loop_depth;
for (insn = bb->head; insn != NEXT_INSN (bb->end); insn = NEXT_INSN (insn))
REG_EQUAL note on the insn. Since this note would be redundant,
there's no point creating it earlier than here. */
if (! note && ! rtx_varies_p (src, 0))
- REG_NOTES (insn)
- = note = gen_rtx_EXPR_LIST (REG_EQUAL, src, REG_NOTES (insn));
+ note = set_unique_reg_note (insn, REG_EQUAL, src);
/* Don't bother considering a REG_EQUAL note containing an EXPR_LIST
since it represents a function call */
recorded_label_ref = 1;
reg_equiv[regno].replacement = XEXP (note, 0);
- reg_equiv[regno].src = src;
+ reg_equiv[regno].src_p = &SET_SRC (set);
reg_equiv[regno].loop_depth = loop_depth;
/* Don't mess with things live during setjmp. */
within the same loop (or in an inner loop), then move the register
initialization just before the use, so that they are in the same
basic block. */
- for (block = n_basic_blocks - 1; block >= 0; block--)
+ FOR_EACH_BB_REVERSE (bb)
{
- basic_block bb = BASIC_BLOCK (block);
-
loop_depth = bb->loop_depth;
for (insn = bb->end; insn != PREV_INSN (bb->head); insn = PREV_INSN (insn))
{
if (asm_noperands (PATTERN (equiv_insn)) < 0
&& validate_replace_rtx (regno_reg_rtx[regno],
- reg_equiv[regno].src, insn))
+ *(reg_equiv[regno].src_p), insn))
{
rtx equiv_link;
rtx last_link;
remove_death (regno, insn);
REG_N_REFS (regno) = 0;
REG_FREQ (regno) = 0;
- PUT_CODE (equiv_insn, NOTE);
- NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (equiv_insn) = 0;
-
+ delete_insn (equiv_insn);
+
reg_equiv[regno].init_insns
= XEXP (reg_equiv[regno].init_insns, 1);
}
otherwise eliminate_regs_in_insn will abort. */
INSN_CODE (new_insn) = INSN_CODE (equiv_insn);
- PUT_CODE (equiv_insn, NOTE);
- NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (equiv_insn) = 0;
+ delete_insn (equiv_insn);
XEXP (reg_equiv[regno].init_insns, 0) = new_insn;
- REG_BASIC_BLOCK (regno) = block >= 0 ? block : 0;
+ REG_BASIC_BLOCK (regno) = bb->index;
REG_N_CALLS_CROSSED (regno) = 0;
REG_LIVE_LENGTH (regno) = 2;
- if (block >= 0 && insn == BLOCK_HEAD (block))
- BLOCK_HEAD (block) = PREV_INSN (insn);
+ if (insn == bb->head)
+ bb->head = PREV_INSN (insn);
/* Remember to clear REGNO from all basic block's live
info. */
/* Clear all dead REGNOs from all basic block's live info. */
if (clear_regnos)
{
- int j, l;
+ int j;
if (clear_regnos > 8)
- {
- for (l = 0; l < n_basic_blocks; l++)
+ {
+ FOR_EACH_BB (bb)
{
- AND_COMPL_REG_SET (BASIC_BLOCK (l)->global_live_at_start,
- &cleared_regs);
- AND_COMPL_REG_SET (BASIC_BLOCK (l)->global_live_at_end,
- &cleared_regs);
+ AND_COMPL_REG_SET (bb->global_live_at_start, &cleared_regs);
+ AND_COMPL_REG_SET (bb->global_live_at_end, &cleared_regs);
}
}
else
- EXECUTE_IF_SET_IN_REG_SET (&cleared_regs, 0, j,
- {
- for (l = 0; l < n_basic_blocks; l++)
+ EXECUTE_IF_SET_IN_REG_SET (&cleared_regs, 0, j,
+ {
+ FOR_EACH_BB (bb)
{
- CLEAR_REGNO_REG_SET (BASIC_BLOCK (l)->global_live_at_start, j);
- CLEAR_REGNO_REG_SET (BASIC_BLOCK (l)->global_live_at_end, j);
+ CLEAR_REGNO_REG_SET (bb->global_live_at_start, j);
+ CLEAR_REGNO_REG_SET (bb->global_live_at_end, j);
}
});
}
}
/* Mark REG as having no known equivalence.
- Some instructions might have been proceessed before and furnished
+ Some instructions might have been processed before and furnished
with REG_EQUIV notes for this register; these notes will have to be
removed.
STORE is the piece of RTL that does the non-constant / conflicting
block_alloc (b)
int b;
{
- register int i, q;
- register rtx insn;
- rtx note;
+ int i, q;
+ rtx insn;
+ rtx note, hard_reg;
int insn_number = 0;
int insn_count = 0;
int max_uid = get_max_uid ();
if (INSN_P (insn))
{
- register rtx link, set;
- register int win = 0;
- register rtx r0, r1 = NULL_RTX;
+ rtx link, set;
+ int win = 0;
+ rtx r0, r1 = NULL_RTX;
int combined_regno = -1;
int i;
for (i = 1; i < recog_data.n_operands; i++)
{
const char *p = recog_data.constraints[i];
- int this_match = (requires_inout (p));
+ int this_match = requires_inout (p);
n_matching_alts += this_match;
if (this_match == recog_data.n_alternatives)
must match operand zero. In that case, skip any
operand that doesn't list operand 0 since we know that
the operand always conflicts with operand 0. We
- ignore commutatity in this case to keep things simple. */
+ ignore commutativity in this case to keep things simple. */
if (n_matching_alts == recog_data.n_alternatives
&& 0 == requires_inout (recog_data.constraints[i]))
continue;
/* If the operand is an address, find a register in it.
There may be more than one register, but we only try one
of them. */
- if (recog_data.constraints[i][0] == 'p')
+ if (recog_data.constraints[i][0] == 'p'
+ || EXTRA_ADDRESS_CONSTRAINT (recog_data.constraints[i][0],
+ recog_data.constraints[i]))
while (GET_CODE (r1) == PLUS || GET_CODE (r1) == MULT)
r1 = XEXP (r1, 0);
+ /* Avoid making a call-saved register unnecessarily
+ clobbered. */
+ hard_reg = get_hard_reg_initial_reg (cfun, r1);
+ if (hard_reg != NULL_RTX)
+ {
+ if (GET_CODE (hard_reg) == REG
+ && IN_RANGE (REGNO (hard_reg),
+ 0, FIRST_PSEUDO_REGISTER - 1)
+ && ! call_used_regs[REGNO (hard_reg)])
+ continue;
+ }
+
if (GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG)
{
/* We have two priorities for hard register preferences.
discourage the register allocator from creating false
dependencies.
- The adjustment value is choosen to indicate that this qty
+ The adjustment value is chosen to indicate that this qty
conflicts with all the qtys in the instructions immediately
before and after the lifetime of this qty.
const PTR q1p;
const PTR q2p;
{
- register int q1 = *(const int *) q1p, q2 = *(const int *) q2p;
- register int tem = QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
+ int q1 = *(const int *) q1p, q2 = *(const int *) q2p;
+ int tem = QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
if (tem != 0)
return tem;
qty_sugg_compare (q1, q2)
int q1, q2;
{
- register int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
+ int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
if (tem != 0)
return tem;
const PTR q1p;
const PTR q2p;
{
- register int q1 = *(const int *) q1p, q2 = *(const int *) q2p;
- register int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
+ int q1 = *(const int *) q1p, q2 = *(const int *) q2p;
+ int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
if (tem != 0)
return tem;
If we really combined them, we could lose if the pseudo lives
across an insn that clobbers the hard reg (eg, movstr).
- ALREADY_DEAD is non-zero if USEDREG is known to be dead even though
+ ALREADY_DEAD is nonzero if USEDREG is known to be dead even though
there is no REG_DEAD note on INSN. This occurs during the processing
of REG_NO_CONFLICT blocks.
- MAY_SAVE_COPYCOPY is non-zero if this insn is simply copying USEDREG to
+ MAY_SAVE_COPYCOPY is nonzero if this insn is simply copying USEDREG to
SETREG or if the input and output must share a register.
In that case, we record a hard reg suggestion in QTY_PHYS_COPY_SUGG.
rtx insn;
int already_dead;
{
- register int ureg, sreg;
- register int offset = 0;
+ int ureg, sreg;
+ int offset = 0;
int usize, ssize;
- register int sqty;
+ int sqty;
/* Determine the numbers and sizes of registers being used. If a subreg
is present that does not change the entire register, don't consider
while (GET_CODE (usedreg) == SUBREG)
{
- if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (usedreg))) > UNITS_PER_WORD)
- may_save_copy = 0;
- if (REGNO (SUBREG_REG (usedreg)) < FIRST_PSEUDO_REGISTER)
- offset += subreg_regno_offset (REGNO (SUBREG_REG (usedreg)),
- GET_MODE (SUBREG_REG (usedreg)),
- SUBREG_BYTE (usedreg),
- GET_MODE (usedreg));
- else
- offset += (SUBREG_BYTE (usedreg)
- / REGMODE_NATURAL_SIZE (GET_MODE (usedreg)));
- usedreg = SUBREG_REG (usedreg);
+ rtx subreg = SUBREG_REG (usedreg);
+
+ if (GET_CODE (subreg) == REG)
+ {
+ if (GET_MODE_SIZE (GET_MODE (subreg)) > UNITS_PER_WORD)
+ may_save_copy = 0;
+
+ if (REGNO (subreg) < FIRST_PSEUDO_REGISTER)
+ offset += subreg_regno_offset (REGNO (subreg),
+ GET_MODE (subreg),
+ SUBREG_BYTE (usedreg),
+ GET_MODE (usedreg));
+ else
+ offset += (SUBREG_BYTE (usedreg)
+ / REGMODE_NATURAL_SIZE (GET_MODE (usedreg)));
+ }
+
+ usedreg = subreg;
}
+
if (GET_CODE (usedreg) != REG)
return 0;
+
ureg = REGNO (usedreg);
if (ureg < FIRST_PSEUDO_REGISTER)
usize = HARD_REGNO_NREGS (ureg, GET_MODE (usedreg));
while (GET_CODE (setreg) == SUBREG)
{
- if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (setreg))) > UNITS_PER_WORD)
- may_save_copy = 0;
- if (REGNO (SUBREG_REG (setreg)) < FIRST_PSEUDO_REGISTER)
- offset -= subreg_regno_offset (REGNO (SUBREG_REG (setreg)),
- GET_MODE (SUBREG_REG (setreg)),
- SUBREG_BYTE (setreg),
- GET_MODE (setreg));
- else
- offset -= (SUBREG_BYTE (setreg)
- / REGMODE_NATURAL_SIZE (GET_MODE (setreg)));
- setreg = SUBREG_REG (setreg);
+ rtx subreg = SUBREG_REG (setreg);
+
+ if (GET_CODE (subreg) == REG)
+ {
+ if (GET_MODE_SIZE (GET_MODE (subreg)) > UNITS_PER_WORD)
+ may_save_copy = 0;
+
+ if (REGNO (subreg) < FIRST_PSEUDO_REGISTER)
+ offset -= subreg_regno_offset (REGNO (subreg),
+ GET_MODE (subreg),
+ SUBREG_BYTE (setreg),
+ GET_MODE (setreg));
+ else
+ offset -= (SUBREG_BYTE (setreg)
+ / REGMODE_NATURAL_SIZE (GET_MODE (setreg)));
+ }
+
+ setreg = subreg;
}
+
if (GET_CODE (setreg) != REG)
return 0;
+
sreg = REGNO (setreg);
if (sreg < FIRST_PSEUDO_REGISTER)
ssize = HARD_REGNO_NREGS (sreg, GET_MODE (setreg));
qty[sqty].freq += REG_FREQ (sreg);
if (usize < ssize)
{
- register int i;
+ int i;
for (i = qty[sqty].first_reg; i >= 0; i = reg_next_in_qty[i])
reg_offset[i] -= offset;
int reg;
enum reg_class class;
{
- register enum reg_class rclass = reg_preferred_class (reg);
+ enum reg_class rclass = reg_preferred_class (reg);
return (reg_class_subset_p (rclass, class)
|| reg_class_subset_p (class, rclass));
}
rclass = reg_alternate_class (reg);
if (reg_class_subset_p (rclass, qty[qtyno].alternate_class))
qty[qtyno].alternate_class = rclass;
-
- if (REG_CHANGES_MODE (reg))
- qty[qtyno].changes_mode = 1;
}
\f
/* Handle something which alters the value of an rtx REG.
rtx reg;
int birth;
{
- register int regno;
+ int regno;
if (GET_CODE (reg) == SUBREG)
{
}
}
-/* Record the death of REG in the current insn. If OUTPUT_P is non-zero,
+/* Record the death of REG in the current insn. If OUTPUT_P is nonzero,
REG is an output that is dying (i.e., it is never used), otherwise it
is an input (the normal case).
If OUTPUT_P is 1, then we extend the life past the end of this insn. */
static void
wipe_dead_reg (reg, output_p)
- register rtx reg;
+ rtx reg;
int output_p;
{
- register int regno = REGNO (reg);
+ int regno = REGNO (reg);
/* If this insn has multiple results,
and the dead reg is used in one of the results,
If QTYNO crosses calls, insist on a register preserved by calls,
unless ACCEPT_CALL_CLOBBERED is nonzero.
- If JUST_TRY_SUGGESTED is non-zero, only try to see if the suggested
+ If JUST_TRY_SUGGESTED is nonzero, only try to see if the suggested
register is available. If not, return -1. */
static int
int just_try_suggested;
int born_index, dead_index;
{
- register int i, ins;
-#ifdef HARD_REG_SET
- /* Declare it register if it's a scalar. */
- register
-#endif
- HARD_REG_SET used, first_used;
+ int i, ins;
+ HARD_REG_SET first_used, used;
#ifdef ELIMINABLE_REGS
- static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
+ static const struct {const int from, to; } eliminables[] = ELIMINABLE_REGS;
#endif
/* Validate our parameters. */
SET_HARD_REG_BIT (used, FRAME_POINTER_REGNUM);
#endif
-#ifdef CLASS_CANNOT_CHANGE_MODE
- if (qty[qtyno].changes_mode)
- IOR_HARD_REG_SET (used,
- reg_class_contents[(int) CLASS_CANNOT_CHANGE_MODE]);
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ cannot_change_mode_set_regs (&used, mode, qty[qtyno].first_reg);
#endif
/* Normally, the registers that can be used for the first register in
|| accept_call_clobbered
|| ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
{
- register int j;
- register int size1 = HARD_REGNO_NREGS (regno, mode);
+ int j;
+ int size1 = HARD_REGNO_NREGS (regno, mode);
for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, regno + j); j++);
if (j == size1)
{
}
\f
/* Mark that REGNO with machine-mode MODE is live starting from the current
- insn (if LIFE is non-zero) or dead starting at the current insn (if LIFE
+ insn (if LIFE is nonzero) or dead starting at the current insn (if LIFE
is zero). */
static void
mark_life (regno, mode, life)
- register int regno;
+ int regno;
enum machine_mode mode;
int life;
{
- register int j = HARD_REGNO_NREGS (regno, mode);
+ int j = HARD_REGNO_NREGS (regno, mode);
if (life)
while (--j >= 0)
SET_HARD_REG_BIT (regs_live, regno + j);
}
/* Mark register number REGNO (with machine-mode MODE) as live (if LIFE
- is non-zero) or dead (if LIFE is zero) from insn number BIRTH (inclusive)
+ is nonzero) or dead (if LIFE is zero) from insn number BIRTH (inclusive)
to insn number DEATH (exclusive). */
static void
enum machine_mode mode;
int life, birth, death;
{
- register int j = HARD_REGNO_NREGS (regno, mode);
+ int j = HARD_REGNO_NREGS (regno, mode);
#ifdef HARD_REG_SET
/* Declare it register if it's a scalar. */
register
int found_zero = 0;
int reg_allowed = 0;
int num_matching_alts = 0;
+ int len;
- while ((c = *p++))
- switch (c)
- {
- case '=': case '+': case '?':
- case '#': case '&': case '!':
- case '*': case '%':
- case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case 'm': case '<': case '>': case 'V': case 'o':
- case 'E': case 'F': case 'G': case 'H':
- case 's': case 'i': case 'n':
- case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P':
- case 'X':
- /* These don't say anything we care about. */
- break;
+ for ( ; (c = *p); p += len)
+ {
+ len = CONSTRAINT_LEN (c, p);
+ switch (c)
+ {
+ case '=': case '+': case '?':
+ case '#': case '&': case '!':
+ case '*': case '%':
+ case 'm': case '<': case '>': case 'V': case 'o':
+ case 'E': case 'F': case 'G': case 'H':
+ case 's': case 'i': case 'n':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'X':
+ /* These don't say anything we care about. */
+ break;
- case ',':
- if (found_zero && ! reg_allowed)
- num_matching_alts++;
+ case ',':
+ if (found_zero && ! reg_allowed)
+ num_matching_alts++;
- found_zero = reg_allowed = 0;
- break;
+ found_zero = reg_allowed = 0;
+ break;
- case '0':
- found_zero = 1;
- break;
+ case '0':
+ found_zero = 1;
+ break;
- default:
- if (REG_CLASS_FROM_LETTER (c) == NO_REGS)
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ /* Skip the balance of the matching constraint. */
+ do
+ p++;
+ while (ISDIGIT (*p));
+ len = 0;
break;
- /* FALLTHRU */
- case 'p':
- case 'g': case 'r':
- reg_allowed = 1;
- break;
- }
+
+ default:
+ if (REG_CLASS_FROM_CONSTRAINT (c, p) == NO_REGS
+ && !EXTRA_ADDRESS_CONSTRAINT (c, p))
+ break;
+ /* FALLTHRU */
+ case 'p':
+ case 'g': case 'r':
+ reg_allowed = 1;
+ break;
+ }
+ }
if (found_zero && ! reg_allowed)
num_matching_alts++;
dump_local_alloc (file)
FILE *file;
{
- register int i;
+ int i;
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
if (reg_renumber[i] != -1)
fprintf (file, ";; Register %d in %d.\n", i, reg_renumber[i]);